From 33a4316306130f8b82dee87b93ae962110ffb94b Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 21:12:34 +0200 Subject: [PATCH 001/255] Add pub(crate) ability to codegen --- .../ServerCombinedErrorGenerator.kt | 3 ++- .../protocol/ServerProtocolTestGenerator.kt | 5 +++-- .../rust/codegen/rustlang/CargoDependency.kt | 6 +++--- .../rust/codegen/rustlang/RustModule.kt | 8 ++++---- .../smithy/rust/codegen/rustlang/RustTypes.kt | 20 ++++++++++++++----- .../rust/codegen/rustlang/RustWriter.kt | 3 ++- .../rust/codegen/smithy/CodegenDelegator.kt | 3 ++- .../codegen/smithy/SymbolMetadataProvider.kt | 6 +++--- .../generators/NestedAccessorGenerator.kt | 3 ++- .../smithy/generators/PaginatorGenerator.kt | 3 ++- .../client/FluentClientDecorator.kt | 3 ++- .../error/CombinedErrorGenerator.kt | 3 ++- .../error/TopLevelErrorGenerator.kt | 5 +++-- .../protocol/ProtocolTestGenerator.kt | 3 ++- .../codegen/rustlang/InlineDependencyTest.kt | 1 + 15 files changed, 48 insertions(+), 27 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGenerator.kt index df4259643b..f119c20f9e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -37,7 +38,7 @@ class ServerCombinedErrorGenerator( val symbol = operation.errorSymbol(symbolProvider) val meta = RustMetadata( derives = Attribute.Derives(setOf(RuntimeType.Debug)), - public = true + visibility = Visibility.PUBLIC ) writer.rust("/// Error type for the `${operationSymbol.name}` operation.") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 75a3675a57..ba3c1a6b24 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -132,11 +133,11 @@ class ServerProtocolTestGenerator( val operationName = operationSymbol.name val testModuleName = "server_${operationName.toSnakeCase()}_test" val moduleMeta = RustMetadata( - public = false, additionalAttributes = listOf( Attribute.Cfg("test"), Attribute.Custom("allow(unreachable_code, unused_variables)") - ) + ), + visibility = Visibility.PRIVATE ) writer.withModule(testModuleName, moduleMeta) { renderAllTestCases(allTests) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt index 5cec79035a..d5faef19f9 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt @@ -74,15 +74,15 @@ class InlineDependency( name: String, baseDir: String, vararg additionalDependencies: RustDependency - ): InlineDependency = forRustFile(name, baseDir, public = false, *additionalDependencies) + ): InlineDependency = forRustFile(name, baseDir, visibility = Visibility.PRIVATE, *additionalDependencies) fun forRustFile( name: String, baseDir: String, - public: Boolean, + visibility: Visibility, vararg additionalDependencies: RustDependency ): InlineDependency { - val module = RustModule.default(name, public) + val module = RustModule.default(name, visibility) val filename = "$name.rs" // The inline crate is loaded as a dependency on the runtime classpath val rustFile = this::class.java.getResource("/$baseDir/src/$filename") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt index 2ef15cdb39..f1c87f8d11 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt @@ -13,15 +13,15 @@ data class RustModule(val name: String, val rustMetadata: RustMetadata, val docu } companion object { - fun default(name: String, public: Boolean, documentation: String? = null): RustModule { - return RustModule(name, RustMetadata(public = public), documentation) + fun default(name: String, visibility: Visibility, documentation: String? = null): RustModule { + return RustModule(name, RustMetadata(visibility = visibility), documentation) } fun public(name: String, documentation: String? = null): RustModule = - default(name, public = true, documentation = documentation) + default(name, visibility = Visibility.PUBLIC, documentation = documentation) fun private(name: String, documentation: String? = null): RustModule = - default(name, public = false, documentation = documentation) + default(name, visibility = Visibility.PRIVATE, documentation = documentation) val Config = public("config", documentation = "Configuration for the service.") val Error = public("error", documentation = "Errors that can occur when calling the service.") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index 880bf437bc..c74721a124 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -269,13 +269,19 @@ fun RustType.isCopy(): Boolean = when (this) { else -> false } +enum class Visibility { + PRIVATE, + PUBCRATE, + PUBLIC +} + /** - * Meta information about a Rust construction (field, struct, or enum) + * Meta information about a Rust construction (field, struct, or enum). */ data class RustMetadata( val derives: Attribute.Derives = Attribute.Derives.Empty, val additionalAttributes: List = listOf(), - val public: Boolean + val visibility: Visibility = Visibility.PRIVATE ) { fun withDerives(vararg newDerive: RuntimeType): RustMetadata = this.copy(derives = derives.copy(derives = derives.derives + newDerive)) @@ -293,9 +299,13 @@ data class RustMetadata( } fun renderVisibility(writer: RustWriter): RustMetadata { - if (public) { - writer.writeInline("pub ") - } + writer.writeInline( + when (visibility) { + Visibility.PRIVATE -> "" + Visibility.PUBCRATE -> "pub(crate) " + Visibility.PUBLIC -> "pub " + } + ) return this } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt index d7304a2f6a..5404bb8803 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt @@ -11,6 +11,7 @@ import org.jsoup.nodes.Element import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolWriter +import software.amazon.smithy.codegen.core.SymbolWriter.Factory import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.CollectionShape @@ -416,7 +417,7 @@ class RustWriter private constructor( */ fun withModule( moduleName: String, - rustMetadata: RustMetadata = RustMetadata(public = true), + rustMetadata: RustMetadata = RustMetadata(visibility = Visibility.PUBLIC), moduleWriter: RustWriter.() -> Unit ): RustWriter { // In Rust, modules must specify their own imports—they don't have access to the parent scope. diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index f762d79393..3b7ec1049f 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.rustlang.RustDependency import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.smithy.generators.CargoTomlGenerator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.LibRsGenerator @@ -94,7 +95,7 @@ open class RustCrate( ) { injectInlineDependencies() val modules = inner.writers.values.mapNotNull { it.module() }.filter { it != "lib" } - .map { modules[it] ?: RustModule.default(it, false) } + .map { modules[it] ?: RustModule.default(it, visibility = Visibility.PRIVATE) } inner.finalize( settings, model, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt index 8113290be5..40425b101c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.smithy.RuntimeType.Companion.PartialEq +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.util.hasTrait /** @@ -78,11 +78,11 @@ class BaseSymbolMetadataProvider( private val containerDefault = RustMetadata( Attribute.Derives(defaultDerives.toSet()), additionalAttributes = additionalAttributes, - public = true + visibility = Visibility.PUBLIC ) override fun memberMeta(memberShape: MemberShape): RustMetadata { - return RustMetadata(public = true) + return RustMetadata(visibility = Visibility.PUBLIC) } override fun structureMeta(structureShape: StructureShape): RustMetadata { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/NestedAccessorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/NestedAccessorGenerator.kt index d6ad3d56c0..587c2d8763 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/NestedAccessorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/NestedAccessorGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate @@ -23,7 +24,7 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.lensName /** Generator for accessing nested fields through optional values **/ class NestedAccessorGenerator(private val symbolProvider: RustSymbolProvider) { - private val module = RustModule("lens", RustMetadata(public = false), "Generated accessors for nested fields") + private val module = RustModule("lens", RustMetadata(visibility = Visibility.PUBLIC), "Generated accessors for nested fields") /** * Generate an accessor on [root] that consumes [root] and returns an `Option` for the nested item */ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt index 916d3657be..8fa7a128f1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.render @@ -76,7 +77,7 @@ class PaginatorGenerator private constructor( idx.getPaginationInfo(service, operation).orNull() ?: PANIC("failed to load pagination info") private val module = RustModule( "paginator", - RustMetadata(public = true), + RustMetadata(visibility = Visibility.PUBLIC), documentation = "Paginators for the service" ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt index d70cdc56ff..2ff0d6b5aa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asArgumentType import software.amazon.smithy.rust.codegen.rustlang.asOptional @@ -297,7 +298,7 @@ class FluentClientGenerator( val clientModule = RustModule( "client", - RustMetadata(public = true), + RustMetadata(visibility = Visibility.PUBLIC), documentation = "Client and fluent builders for calling the service." ) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt index d5f959d559..5e1134ceff 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -62,7 +63,7 @@ class CombinedErrorGenerator( val meta = RustMetadata( derives = Attribute.Derives(setOf(RuntimeType.Debug)), additionalAttributes = listOf(Attribute.NonExhaustive), - public = true + visibility = Visibility.PUBLIC ) writer.rust("/// Error type for the `${operationSymbol.name}` operation.") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt index a46e76b9f9..0a84693e72 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -46,7 +47,7 @@ class TopLevelErrorGenerator(codegenContext: CodegenContext, private val operati private val sdkError = CargoDependency.SmithyHttp(codegenContext.runtimeConfig).asType().member("result::SdkError") fun render(crate: RustCrate) { - crate.withModule(RustModule.default("error_meta", false)) { writer -> + crate.withModule(RustModule.default("error_meta", visibility = Visibility.PRIVATE)) { writer -> writer.renderDefinition() writer.renderImplDisplay() // Every operation error can be converted into service::Error @@ -102,7 +103,7 @@ class TopLevelErrorGenerator(codegenContext: CodegenContext, private val operati rust("/// All possible error types for this service.") RustMetadata( additionalAttributes = listOf(Attribute.NonExhaustive), - public = true + visibility = Visibility.PUBLIC ).withDerives(RuntimeType.Debug).render(this) rustBlock("enum Error") { allErrors.forEach { error -> diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGenerator.kt index da57afdca8..794b119b05 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -107,7 +108,7 @@ class ProtocolTestGenerator( val operationName = operationSymbol.name val testModuleName = "${operationName.toSnakeCase()}_request_test" val moduleMeta = RustMetadata( - public = false, + visibility = Visibility.PRIVATE, additionalAttributes = listOf( Attribute.Cfg("test"), Attribute.Custom("allow(unreachable_code, unused_variables)") diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt index bc9569856f..98015a7857 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt @@ -29,6 +29,7 @@ internal class InlineDependencyTest { fun `locate dependencies from the inlineable module`() { val dep = InlineDependency.idempotencyToken() val testWriter = RustWriter.root() + testWriter.compileAndTest() testWriter.addDependency(CargoDependency.FastRand) testWriter.withModule(dep.module.name) { dep.renderer(this) From 5a31894ef62c4612f76f06a008186ea0a66b7f5c Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 23 Feb 2022 12:04:21 +0100 Subject: [PATCH 002/255] SIMPLE SIMPLE MODEL --- codegen-server-test/model/simple.smithy | 128 ++++-------------------- 1 file changed, 17 insertions(+), 111 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index d4420787fe..31fdf9e0d0 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -3,129 +3,35 @@ $version: "1.0" namespace com.amazonaws.simple use aws.protocols#restJson1 -use smithy.test#httpRequestTests -use smithy.test#httpResponseTests @restJson1 @title("SimpleService") -@documentation("A simple service example, with a Service resource that can be registered and a readonly healthcheck") service SimpleService { - version: "2022-01-01", - resources: [ - Service, - ], operations: [ - Healthcheck, - StoreServiceBlob, + AnOperation, ], } -@documentation("Id of the service that will be registered") -string ServiceId - -@documentation("Name of the service that will be registered") -string ServiceName - -@error("client") -@documentation( - """ - Returned when a new resource cannot be created because one already exists. - """ -) -structure ResourceAlreadyExists { - @required - message: String -} - -@documentation("A resource that can register services") -resource Service { - identifiers: { id: ServiceId }, - put: RegisterService, -} - -@idempotent -@http(method: "PUT", uri: "/service/{id}") -@documentation("Service register operation") -@httpRequestTests([ - { - id: "RegisterServiceRequestTest", - protocol: "aws.protocols#restJson1", - uri: "/service/1", - headers: { - "Content-Type": "application/json", - }, - params: { id: "1", name: "TestService" }, - body: "{\"name\":\"TestService\"}", - method: "PUT", - } -]) -@httpResponseTests([ - { - id: "RegisterServiceResponseTest", - protocol: "aws.protocols#restJson1", - params: { id: "1", name: "TestService" }, - body: "{\"id\":\"1\",\"name\":\"TestService\"}", - code: 200, - } -]) -operation RegisterService { - input: RegisterServiceInputRequest, - output: RegisterServiceOutputResponse, - errors: [ResourceAlreadyExists] -} - -@documentation("Service register input structure") -structure RegisterServiceInputRequest { - @required - @httpLabel - id: ServiceId, - name: ServiceName, -} - -@documentation("Service register output structure") -structure RegisterServiceOutputResponse { - @required - id: ServiceId, - name: ServiceName, -} - -@readonly -@http(uri: "/healthcheck", method: "GET") -@documentation("Read-only healthcheck operation") -operation Healthcheck { - input: HealthcheckInputRequest, - output: HealthcheckOutputResponse +@http(uri: "/operation", method: "GET") +operation AnOperation { + input: AnOperationInput, + output: AnOperationOutput, + errors: [MyError] } -@documentation("Service healthcheck output structure") -structure HealthcheckInputRequest { - +structure AnOperationInput { + @httpResponseCode + responseCode: Integer } -@documentation("Service healthcheck input structure") -structure HealthcheckOutputResponse { - +structure AnOperationOutput { + @httpResponseCode + responseCode: Integer } -@readonly -@http(method: "GET", uri: "/service/{id}/blob") -@documentation("Stores a blob for a service id") -operation StoreServiceBlob { - input: StoreServiceBlobInput, - output: StoreServiceBlobOutput -} - -@documentation("Store a blob for a service id input structure") -structure StoreServiceBlobInput { - @required - @httpLabel - id: ServiceId, - @required - @httpPayload - content: Blob, -} - -@documentation("Store a blob for a service id output structure") -structure StoreServiceBlobOutput { - +@error("client") +@httpError(404) +structure MyError { + @httpResponseCode + responseCode: Integer } From e7eb5b728956e8952a64ce46b38b4a5143280357 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 1 Apr 2022 16:33:37 +0200 Subject: [PATCH 003/255] Make simple.smithy like my playground --- codegen-server-test/model/simple.smithy | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 31fdf9e0d0..3192726153 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -16,22 +16,30 @@ service SimpleService { operation AnOperation { input: AnOperationInput, output: AnOperationOutput, - errors: [MyError] } structure AnOperationInput { - @httpResponseCode - responseCode: Integer + @required + conA: ConA } structure AnOperationOutput { - @httpResponseCode - responseCode: Integer + conA: ConA } -@error("client") -@httpError(404) -structure MyError { - @httpResponseCode - responseCode: Integer +structure ConA { + @required + conB: ConB, + + optConB: ConB +} + +structure ConB { + @required + nice: String, + @required + int: Integer, + + optNice: String, + optInt: Integer } From 89437d76fb02d9d6442484839c884ea1ae14c1fa Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 5 Apr 2022 14:52:09 +0200 Subject: [PATCH 004/255] sst and srt works; recursive not handled, 4 failing tests in srt --- .../server/smithy/ServerCodegenVisitor.kt | 5 +- .../generators/ServerBuilderGenerator.kt | 436 ++++++++++++++++++ .../ServerHttpBoundProtocolGenerator.kt | 63 ++- .../rust/codegen/rustlang/CargoDependency.kt | 3 + .../smithy/rust/codegen/rustlang/RustTypes.kt | 40 +- .../rust/codegen/smithy/RuntimeTypes.kt | 8 +- .../rust/codegen/smithy/SymbolVisitor.kt | 25 +- .../smithy/generators/BuilderGenerator.kt | 64 ++- .../codegen/smithy/generators/Instantiator.kt | 11 +- .../smithy/generators/StructureGenerator.kt | 65 ++- .../protocols/parse/JsonParserGenerator.kt | 50 +- .../aws-smithy-http-server/src/rejection.rs | 3 + .../aws-smithy-http-server/src/validation.rs | 7 + rust-runtime/inlineable/src/lib.rs | 2 + rust-runtime/inlineable/src/validation.rs | 10 + 15 files changed, 683 insertions(+), 109 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt create mode 100644 rust-runtime/aws-smithy-http-server/src/validation.rs create mode 100644 rust-runtime/inlineable/src/validation.rs diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 28733d2047..1cfa6445a3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext @@ -26,7 +27,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator @@ -166,8 +166,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val builderGenerator = - BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) + val builderGenerator = ServerBuilderGenerator(codegenContext, shape) builderGenerator.render(writer) writer.implBlock(shape, symbolProvider) { builderGenerator.renderConvenienceMethod(this) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt new file mode 100644 index 0000000000..1441ebb6f7 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -0,0 +1,436 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.rustlang.docs +import software.amazon.smithy.rust.codegen.rustlang.documentShape +import software.amazon.smithy.rust.codegen.rustlang.implInto +import software.amazon.smithy.rust.codegen.rustlang.render +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.rustlang.withBlock +import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.targetNeedsValidation +import software.amazon.smithy.rust.codegen.smithy.isOptional +import software.amazon.smithy.rust.codegen.smithy.makeOptional +import software.amazon.smithy.rust.codegen.smithy.mapRustType +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.wrapValidated +import software.amazon.smithy.rust.codegen.util.dq +import software.amazon.smithy.rust.codegen.util.toPascalCase +import software.amazon.smithy.rust.codegen.util.toSnakeCase +import java.util.* + +// TODO This function is the same as `BuilderGenerator.kt.` +fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): RuntimeType { + val symbol = symbolProvider.toSymbol(this) + val builderNamespace = RustReservedWords.escapeIfNeeded(symbol.name.toSnakeCase()) + return RuntimeType("Builder", null, "${symbol.namespace}::$builderNamespace") +} + +fun RuntimeConfig.operationBuildError() = RuntimeType.operationModule(this).member("BuildError") +fun RuntimeConfig.serializationError() = RuntimeType.operationModule(this).member("SerializationError") + +class OperationBuildError(private val runtimeConfig: RuntimeConfig) { + fun missingField(w: RustWriter, field: String, details: String) = "${w.format(runtimeConfig.operationBuildError())}::MissingField { field: ${field.dq()}, details: ${details.dq()} }" + fun invalidField(w: RustWriter, field: String, details: String) = "${w.format(runtimeConfig.operationBuildError())}::InvalidField { field: ${field.dq()}, details: ${details.dq()}.to_string() }" + fun serializationError(w: RustWriter, error: String) = "${w.format(runtimeConfig.operationBuildError())}::SerializationError($error.into())" +} + +// TODO Document differences: +// - This one takes in codegenContext. +class ServerBuilderGenerator( + private val codegenContext: CodegenContext, + private val shape: StructureShape +) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val members: List = shape.allMembers.values.toList() + // TODO Ensure everyone uses this instead of recomputing + private val structureSymbol = symbolProvider.toSymbol(shape) + private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() + + fun render(writer: RustWriter) { + writer.docs("See #D.", structureSymbol) + writer.withModule(moduleName) { + renderBuilder(this) + } + + // TODO Can't we move these into the builder module? + if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { + renderTryFromBuilderImpl(writer) + } else { + renderFromBuilderImpl(writer) + } + } + + // TODO This impl does not take into account sensitive trait. + private fun renderImplDisplayValidationFailure(writer: RustWriter) { + writer.rustBlock("impl std::fmt::Display for ValidationFailure") { + rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("match self") { + validationFailures().forEach { + val arm = if (it.hasInner()) { + "ValidationFailure::${it.name()}(_)" + } else { + "ValidationFailure::${it.name()}" + } + rust("""$arm => write!(f, "${validationFailureErrorMessage(it)}"),""") + } + } + } + } + } + + // TODO This only needs to be generated for operation input shapes. + private fun renderImplFromValidationFailureForRequestRejection(writer: RustWriter) { + // TODO No need for rustBlock + writer.rustBlock("impl From for #T", ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig)) { + rustBlock("fn from(value: ValidationFailure) -> Self") { + rust("Self::BuildV2(value.into())") + } + } + } + + private fun renderImplFromBuilderForValidated(writer: RustWriter) { + // TODO No need for rustBlock + writer.rustBlock("impl From for #T", structureSymbol.wrapValidated()) { + rust( + """ + fn from(value: Builder) -> Self { + Self::Unvalidated(value) + } + """ + ) + } + } + + private fun renderBuilder(writer: RustWriter) { + val builderName = "Builder" + + if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { + Attribute.Derives(setOf(RuntimeType.Debug)).render(writer) + // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. + Attribute.NonExhaustive.render(writer) + writer.rustBlock("pub enum ValidationFailure") { + validationFailures().forEach { renderValidationFailure(this, it) } + } + + renderImplDisplayValidationFailure(writer) + writer.rust("impl std::error::Error for ValidationFailure { }") + renderImplFromValidationFailureForRequestRejection(writer) + + renderImplFromBuilderForValidated(writer) + } + + writer.docs("A builder for #D.", structureSymbol) + // Matching derives to the main structure + `Default` since we are a builder and everything is optional. + // TODO Manually implement `Default` so that we can add custom docs. + val baseDerives = structureSymbol.expectRustMetadata().derives + // TODO Document breaking `PartialEq`. + val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default + baseDerives.copy(derives = derives).render(writer) + writer.rustBlock("pub struct $builderName") { + for (member in members) { + // All fields in the builder are optional. + val memberSymbol = builderMemberSymbol(member) + val memberName = symbolProvider.toMemberName(member) + renderBuilderMember(this, memberName, memberSymbol) + } + } + + writer.rustBlock("impl $builderName") { + for (member in members) { + renderBuilderMemberFn(this, member) + + if (member.targetNeedsValidation(model, symbolProvider)) { + renderBuilderMemberSetterFn(this, member) + } + + // Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. + } + renderBuildFn(this) + } + } + + private fun renderBuildFn(implBlockWriter: RustWriter) { + val fallibleBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) + val outputSymbol = symbolProvider.toSymbol(shape) + val returnType = when (fallibleBuilder) { + true -> "Result<${implBlockWriter.format(outputSymbol)}, ValidationFailure>" + false -> implBlockWriter.format(outputSymbol) + } + // TODO Document when builder can fail. + // TODO Document it returns the first error. + implBlockWriter.docs("Consumes the builder and constructs a #D.", outputSymbol) + implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { +// if (members.any { targetNeedsValidation(it) }) { +// implBlockWriter.rust("use std::convert::TryInto;") +// } + conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { + // If a wrapper is specified, use the `::new` associated function to construct the wrapper. + coreBuilder(this) + } + } + } + + fun renderConvenienceMethod(implBlock: RustWriter) { + val builderSymbol = shape.builderSymbol(symbolProvider) + implBlock.docs("Creates a new builder-style object to manufacture #D.", structureSymbol) + implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { + write("#T::default()", builderSymbol) + } + } + + // TODO(EventStream): [DX] Consider updating builders to take EventInputStream as Into + private fun renderBuilderMember(writer: RustWriter, memberName: String, memberSymbol: Symbol) { + // Builder members are crate-public to enable using them directly in serializers/deserializers. + // During XML deserialization, `builder..take` is used to append to lists and maps. + writer.write("pub(crate) $memberName: #T,", memberSymbol) + } + + private fun renderBuilderMemberFn( + writer: RustWriter, + member: MemberShape, + ) { + val symbol = symbolProvider.toSymbol(member) + val memberName = symbolProvider.toMemberName(member) + + writer.documentShape(member, model) + writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { + rust("self.$memberName = ") + conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { + if (member.targetNeedsValidation(model, symbolProvider)) { + val validatedType = "${symbol.wrapValidated().rustType().namespace}::Validated::Validated" + if (symbol.isOptional()) { + write("input.map(|v| $validatedType(v))") + } else { + write("$validatedType(input)") + } + } else { + write("input") + } + } + rust(";") + rust("self") + } + } + + + /* + * Render a `set_foo` method. This method is able to take in builders of structure shape types. + */ + private fun renderBuilderMemberSetterFn( + writer: RustWriter, + member: MemberShape, + ) { + check(model.expectShape(member.target, StructureShape::class.java) != null) + + val builderMemberSymbol = builderMemberSymbol(member) + val inputType = builderMemberSymbol.rustType().stripOuter().implInto().let { + if (member.isOptional) { + "Option<$it>" + } else { + it + } + } + val memberName = symbolProvider.toMemberName(member) + + writer.documentShape(member, model) + // TODO: This method is only used by deserializers, so it will remain unused for shapes that are not (transitively) + // part of an operation input. We therefore `[allow(dead_code)]` here. + Attribute.AllowDeadCode.render(writer) + // TODO(): `pub(crate)` until we commit to making builders of builders public. + // Setter names will never hit a reserved word and therefore never need escaping. + writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { + rust( + """ + self.$memberName = ${ + if (member.isOptional) { + "input.map(|v| v.into())" + } else { + "Some(input.into())" + } + }; + self + """ + ) + } + } + + // TODO Docs + enum class ValidationFailureKind { + MISSING_MEMBER, + BUILDER_FAILURE, + } + data class ValidationFailure(val forMember: MemberShape, val kind: ValidationFailureKind) { + fun name() = when (kind) { + ValidationFailureKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" + ValidationFailureKind.BUILDER_FAILURE -> "${forMember.memberName.toPascalCase()}ValidationFailure" + } + + fun hasInner() = kind == ValidationFailureKind.BUILDER_FAILURE + } + + private fun renderValidationFailure(writer: RustWriter, validationFailure: ValidationFailure) { + if (validationFailure.kind == ValidationFailureKind.BUILDER_FAILURE) { + // TODO(): `#[doc(hidden)]` until we commit to making builders of builders public. + Attribute.DocHidden.render(writer) + } + + // TODO Add Rust docs. + + when (validationFailure.kind) { + ValidationFailureKind.MISSING_MEMBER -> writer.rust("${validationFailure.name()},") + ValidationFailureKind.BUILDER_FAILURE -> { + val targetStructureShape = model.expectShape(validationFailure.forMember.target, StructureShape::class.java) + writer.rust("${validationFailure.name()}(<#{T} as std::convert::TryFrom<#{T}>>::Error),", + symbolProvider.toSymbol(targetStructureShape), + targetStructureShape.builderSymbol(symbolProvider) + ) + } + } + } + + // ONLY RETURNS BUILDER validation failure, intentional + private fun builderValidationFailureForMember(member: MemberShape) = + if (model.expectShape(member.target).isStructureShape) { + Optional.of(ValidationFailure(member, ValidationFailureKind.BUILDER_FAILURE)) + } else { + Optional.empty() + } + + private fun validationFailureErrorMessage(validationFailure: ValidationFailure): String { + val memberName = symbolProvider.toMemberName(validationFailure.forMember) + // TODO $structureSymbol here is not quite right because it's printing the full namespace: crate:: in the context of the user will surely be different. + return when (validationFailure.kind) { + ValidationFailureKind.MISSING_MEMBER -> "`$memberName` was not specified but it is required when building `$structureSymbol`" + // TODO Nest errors. + ValidationFailureKind.BUILDER_FAILURE -> "validation failure occurred when building member `$memberName`, when building `$structureSymbol`" + } + } + + private fun validationFailures() = members.flatMap { member -> + val ret = mutableListOf() + if (mustProvideValueForMember(member)) { + ret.add(ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER)) + } + val builderValidationFailure = builderValidationFailureForMember(member) + if (builderValidationFailure.isPresent) { + ret.add(builderValidationFailure.get()) + } + + // TODO Constrained shapes. + + ret + } + + private fun renderTryFromBuilderImpl(writer: RustWriter) { + val shapeSymbol = symbolProvider.toSymbol(shape) + val builderSymbol = shape.builderSymbol(symbolProvider) + writer.rustTemplate( + """ + impl std::convert::TryFrom<#{Builder}> for #{Shape} { + type Error = $moduleName::ValidationFailure; + + fn try_from(value: #{Builder}) -> Result { + value.build() + } + } + """, + "Builder" to builderSymbol, + "Shape" to shapeSymbol, + ) + } + + private fun renderFromBuilderImpl(writer: RustWriter) { + val shapeSymbol = symbolProvider.toSymbol(shape) + val builderSymbol = shape.builderSymbol(symbolProvider) + writer.rustTemplate( + """ + impl From<#{Builder}> for #{Shape} { + fn from(value: #{Builder}) -> Self { + value.build() + } + } + """, + "Builder" to builderSymbol, + "Shape" to shapeSymbol, + ) + } + + // TODO Docs + private fun builderMemberSymbol(member: MemberShape): Symbol = + symbolProvider.toSymbol(member) + .mapRustType { it.stripOuter() } + .let { + if (member.targetNeedsValidation(model, symbolProvider)) it.wrapValidated() + else it + }.makeOptional() + + /** + * TODO DOCS + */ + private fun coreBuilder(writer: RustWriter) { + // Builder type: + // Option> + // Option + // + // Struct type: + // Option + // T + writer.rustBlock("#T", structureSymbol) { + for (member in members) { + val memberName = symbolProvider.toMemberName(member) + + withBlock("$memberName: self.$memberName", ",") { + // Write the modifier(s). + if (member.targetNeedsValidation(model, symbolProvider)) { + // TODO Remove `TryInto` import when we switch to 2021 edition. + rustTemplate( + """ + .map(|v| match v { + #{Validated}::Validated(x) => Ok(x), + #{Validated}::Unvalidated(x) => { + use std::convert::TryInto; + x.try_into() + } + }) + .map(|v| v.map_err(|err| ValidationFailure::${builderValidationFailureForMember(member).get().name()}(err))) + .transpose()? + """, + "Validated" to RuntimeType.Validated() + ) + } + if (mustProvideValueForMember(member)) { + rust(".ok_or(ValidationFailure::${ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER).name()})?") + } + } + } + } + } + + // TODO We could move to extension function in `StructureGenerator.kt`. + // TODO Is it.isOptional() necessary? Won't canUseDefault take into account `Option`s already? + private fun mustProvideValueForMember(member: MemberShape) = + !symbolProvider.toSymbol(member).isOptional() +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index f2688f7971..49ae27a1ac 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -42,18 +42,18 @@ import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolTraitImplGenerator -import software.amazon.smithy.rust.codegen.smithy.generators.setterName +import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.parse.StructuredDataParserGenerator -import software.amazon.smithy.rust.codegen.smithy.toOptional import software.amazon.smithy.rust.codegen.smithy.wrapOptional import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.expectTrait @@ -599,15 +599,34 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val member = binding.member val parsedValue = serverRenderBindingParser(binding, operationShape, httpBindingGenerator, structuredDataParser) if (parsedValue != null) { - withBlock("input = input.${member.setterName()}(", ");") { - parsedValue(this) - } + rust("if let Some(value) = ") + parsedValue(this) + rust( + """ + { + input = input.${member.deserializerBuilderSetterName(model, symbolProvider)}(${ + if (symbolProvider.toSymbol(binding.member).isOptional()) { + "Some(value)" + } else { + "value" + } + }); + } + """ + ) +// withBlock("input = input.${member.deserializerBuilderSetterName(model, symbolProvider)}(", ");") { +// if (symbolProvider.toSymbol(binding.member).isOptional()) { +// "Some(${parsedValue(this)})" +// } else { +// parsedValue(this) +// } +// } } } serverRenderUriPathParser(this, operationShape) serverRenderQueryStringParser(this, operationShape) - val err = if (StructureGenerator.fallibleBuilder(inputShape, symbolProvider)) { + val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider)) { "?" } else "" rustTemplate("input.build()$err", *codegenScope) @@ -743,8 +762,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val deserializer = generateParsePercentEncodedStrFn(binding) rustTemplate( """ - input = input.${binding.member.setterName()}( - ${symbolProvider.toOptional(binding.member, "#{deserializer}(m$index)?")} + input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider)}( + #{deserializer}(m$index)? ); """.trimIndent(), *codegenScope, @@ -840,8 +859,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !seen_$memberName && k == "${it.locationName}" { - input = input.${it.member.setterName()}( - ${symbolProvider.toOptional(it.member, "#{deserializer}(&v)?")} + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider)}( + #{deserializer}(&v)? ); seen_$memberName = true; } @@ -913,19 +932,27 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } if (queryParamsBinding != null) { - rust("input = input.${queryParamsBinding.member.setterName()}(Some(query_params));") + rust("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider)}(${ + if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { + "Some(query_params)" + } else { + "query_params" + } + });") } queryBindingsTargettingCollection.forEach { val memberName = symbolProvider.toMemberName(it.member) rustTemplate( """ - input = input.${it.member.setterName()}( - if $memberName.is_empty() { - None - } else { - Some($memberName) - } - ); + if !$memberName.is_empty() { + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider)}(${ + if (symbolProvider.toSymbol(it.member).isOptional()) { + "Some($memberName)" + } else { + memberName + } + }); + } """.trimIndent() ) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt index 5cec79035a..f16eb5da05 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt @@ -112,6 +112,9 @@ class InlineDependency( fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency = forRustFile("rest_xml_unwrapped_errors", CargoDependency.smithyXml(runtimeConfig)) + + fun validation(): InlineDependency = + forRustFile("validation") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index 880bf437bc..b0cc8269aa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.rustlang +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.util.dq @@ -91,11 +92,11 @@ sealed class RustType { } data class Reference(val lifetime: kotlin.String?, override val member: RustType) : RustType(), Container { - override val name: kotlin.String = member.name + override val name = member.name } data class Option(override val member: RustType) : RustType(), Container { - override val name: kotlin.String = "Option" + override val name = "Option" override val namespace = "std::option" /** Convert `Option` to `Option<&T>` **/ @@ -104,8 +105,14 @@ sealed class RustType { } } + data class Validated(override val member: RustType): RustType(), Container { + val runtimeType: RuntimeType = RuntimeType.Validated() + override val name = runtimeType.name!! + override val namespace = runtimeType.namespace + } + data class Box(override val member: RustType) : RustType(), Container { - override val name: kotlin.String = "Box" + override val name = "Box" override val namespace = "std::boxed" } @@ -115,7 +122,7 @@ sealed class RustType { } data class Vec(override val member: RustType) : RustType(), Container { - override val name: kotlin.String = "Vec" + override val name = "Vec" override val namespace = "std::vec" } @@ -148,25 +155,21 @@ fun RustType.asArgumentType(fullyQualified: Boolean = true): String { } /** Format this Rust type so that it may be used as an argument type in a function definition */ -fun RustType.asArgumentValue(name: String): String { - return when (this) { - is RustType.String, - is RustType.Box -> "$name.into()" +fun RustType.asArgumentValue(name: String) = + when (this) { + is RustType.String, is RustType.Box -> "$name.into()" else -> name } -} /** * For a given name, generate an `Argument` data class containing pre-formatted strings for using this type when - * writing a Rust function + * writing a Rust function. */ -fun RustType.asArgument(name: String): Argument { - return Argument( - "$name: ${this.asArgumentType()}", - this.asArgumentValue(name), - this.render(), - ) -} +fun RustType.asArgument(name: String) = Argument( + "$name: ${this.asArgumentType()}", + this.asArgumentValue(name), + this.render(), +) /** * Render this type, including references and generic parameters. @@ -191,6 +194,7 @@ fun RustType.render(fullyQualified: Boolean = true): String { is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" is RustType.Opaque -> this.name + is RustType.Validated -> "${this.name}<${this.member.render(fullyQualified)}>" } return "$namespace$base" } @@ -329,6 +333,8 @@ sealed class Attribute { val NonExhaustive = Custom("non_exhaustive") val AllowUnusedMut = Custom("allow(unused_mut)") val DocInline = Custom("doc(inline)") + val DocHidden = Custom("doc(hidden)") + val AllowDeadCode = Custom("allow(dead_code)") } data class Derives(val derives: Set) : Attribute() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt index dc26db7ec8..cc0489ba9c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt @@ -226,6 +226,9 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n func, CargoDependency.SmithyProtocolTestHelpers(runtimeConfig), "aws_smithy_protocol_test" ) + fun ValidateTrait() = RuntimeType("Validate", InlineDependency.validation(), namespace = "crate::validation") + fun Validated() = RuntimeType("Validated", InlineDependency.validation(), namespace = "crate::validation") + val http = CargoDependency.Http.asType() fun Http(path: String): RuntimeType = RuntimeType(name = path, dependency = CargoDependency.Http, namespace = "http") @@ -233,8 +236,6 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n val HttpRequestBuilder = Http("request::Builder") val HttpResponseBuilder = Http("response::Builder") - val Hyper = CargoDependency.Hyper.asType() - fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = RuntimeType( "Receiver", @@ -242,8 +243,7 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n "aws_smithy_http::event_stream" ) - fun jsonErrors(runtimeConfig: RuntimeConfig) = - forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) + fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index d00e2ac3d7..370b37409e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -96,23 +96,32 @@ val Outputs = SymbolLocation("output") * * This is idempotent and will have no change if the type is already optional. */ -fun Symbol.makeOptional(): Symbol { - return if (isOptional()) { +fun Symbol.makeOptional(): Symbol = + if (isOptional()) { this } else { val rustType = RustType.Option(this.rustType()) - Symbol.builder().rustType(rustType) + Symbol.builder() .rustType(rustType) .addReference(this) .name(rustType.name) .build() } + +fun Symbol.wrapValidated(): Symbol { + val rustType = RustType.Validated(this.rustType()) + return Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() } /** Map the RustType of a symbol with [f] */ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { val newType = f(this.rustType()) - return Symbol.builder().rustType(newType) + return Symbol.builder() + .rustType(newType) .addReference(this) .name(newType.name) .build() @@ -363,9 +372,7 @@ private const val SHAPE_KEY = "shape" private const val SYMBOL_DEFAULT = "symboldefault" private const val RENAMED_FROM_KEY = "renamedfrom" -fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder { - return this.putProperty(RUST_TYPE_KEY, rustType) -} +fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder = this.putProperty(RUST_TYPE_KEY, rustType) fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder { return this.putProperty(RENAMED_FROM_KEY, name) @@ -374,9 +381,7 @@ fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder { fun Symbol.renamedFrom(): String? = this.getProperty(RENAMED_FROM_KEY, String::class.java).orNull() fun Symbol.defaultValue(): Default = this.getProperty(SYMBOL_DEFAULT, Default::class.java).orElse(Default.NoDefault) -fun Symbol.Builder.setDefault(default: Default): Symbol.Builder { - return this.putProperty(SYMBOL_DEFAULT, default) -} +fun Symbol.Builder.setDefault(default: Default): Symbol.Builder = this.putProperty(SYMBOL_DEFAULT, default) /** * Type representing the default value for a given type. (eg. for Strings, this is `""`) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index 7d0afd2c24..378bc7fa3b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -9,12 +9,10 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asArgument -import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape @@ -56,8 +54,8 @@ class OperationBuildError(private val runtimeConfig: RuntimeConfig) { fun serializationError(w: RustWriter, error: String) = "${w.format(runtimeConfig.operationBuildError())}::SerializationError($error.into())" } -/** setter names will never hit a reserved word and therefore never need escaping */ -fun MemberShape.setterName(): String = "set_${this.memberName.toSnakeCase()}" +// Setter names will never hit a reserved word and therefore never need escaping. +fun MemberShape.setterName() = "set_${this.memberName.toSnakeCase()}" class BuilderGenerator( private val model: Model, @@ -70,21 +68,21 @@ class BuilderGenerator( fun render(writer: RustWriter) { val symbol = symbolProvider.toSymbol(shape) - writer.docs("See #D", symbol) + writer.docs("See #D.", symbol) val segments = shape.builderSymbol(symbolProvider).namespace.split("::") writer.withModule(segments.last()) { renderBuilder(this) } } - private fun buildFn(implBlockWriter: RustWriter) { + private fun renderBuildFn(implBlockWriter: RustWriter) { val fallibleBuilder = StructureGenerator.fallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { - true -> "std::result::Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" + true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" false -> implBlockWriter.format(outputSymbol) } - implBlockWriter.docs("Consumes the builder and constructs a #D", outputSymbol) + implBlockWriter.docs("Consumes the builder and constructs a #D.", outputSymbol) implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { // If a wrapper is specified, use the `::new` associated function to construct the wrapper @@ -94,20 +92,16 @@ class BuilderGenerator( } private fun RustWriter.missingRequiredField(field: String) { - val detailedMessage = "$field was not specified but it is required when building ${ - symbolProvider.toSymbol( - shape - ).name - }" + val detailedMessage = "$field was not specified but it is required when building ${symbolProvider.toSymbol(shape).name}" rust( - """#T::MissingField { field: ${field.dq()}, details: ${detailedMessage.dq()}}""", + """#T::MissingField { field: "$field", details: "$detailedMessage" } """, runtimeConfig.operationBuildError() ) } fun renderConvenienceMethod(implBlock: RustWriter) { val builderSymbol = shape.builderSymbol(symbolProvider) - implBlock.docs("Creates a new builder-style object to manufacture #D", structureSymbol) + implBlock.docs("Creates a new builder-style object to manufacture #D.", structureSymbol) implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { write("#T::default()", builderSymbol) } @@ -115,9 +109,8 @@ class BuilderGenerator( // TODO(EventStream): [DX] Consider updating builders to take EventInputStream as Into private fun renderBuilderMember(writer: RustWriter, memberName: String, memberSymbol: Symbol) { - // builder members are crate-public to enable using them - // directly in serializers/deserializers. During XML deserialization, `builder..take` is used to append to - // lists and maps + // Builder members are crate-public to enable using them directly in serializers/deserializers. + // During XML deserialization, `builder..take` is used to append to lists and maps. writer.write("pub(crate) $memberName: #T,", memberSymbol) } @@ -144,9 +137,8 @@ class BuilderGenerator( ) { // Render a `set_foo` method. This is useful as a target for code generation, because the argument type // is the same as the resulting member type, and is always optional. - val inputType = outerType.asOptional() writer.documentShape(member, model) - writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") { + writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${outerType.render()}) -> Self") { rust("self.$memberName = input; self") } } @@ -154,31 +146,29 @@ class BuilderGenerator( private fun renderBuilder(writer: RustWriter) { val builderName = "Builder" - val symbol = structureSymbol - writer.docs("A builder for #D", symbol) - Attribute.NonExhaustive.render(writer) - // Matching derives to the main structure + `Default` since we are a builder and everything is optional - val baseDerives = symbol.expectRustMetadata().derives + writer.docs("A builder for #D.", structureSymbol) + // Matching derives to the main structure + `Default` since we are a builder and everything is optional. + val baseDerives = structureSymbol.expectRustMetadata().derives val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.PartialEq, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) writer.rustBlock("pub struct $builderName") { - members.forEach { member -> + for (member in members) { val memberName = symbolProvider.toMemberName(member) - // All fields in the builder are optional + // All fields in the builder are optional. val memberSymbol = symbolProvider.toSymbol(member).makeOptional() renderBuilderMember(this, memberName, memberSymbol) } } writer.rustBlock("impl $builderName") { - members.forEach { member -> - // All fields in the builder are optional + for (member in members) { + // All fields in the builder are optional. val memberSymbol = symbolProvider.toSymbol(member) val outerType = memberSymbol.rustType() val coreType = outerType.stripOuter() val memberName = symbolProvider.toMemberName(member) // Render a context-aware builder method for certain types, e.g. a method for vectors that automatically - // appends + // appends. when (coreType) { is RustType.Vec -> renderVecHelper(member, memberName, coreType) is RustType.HashMap -> renderMapHelper(member, memberName, coreType) @@ -187,7 +177,7 @@ class BuilderGenerator( renderBuilderMemberSetterFn(this, outerType, member, memberName) } - buildFn(this) + renderBuildFn(this) } } @@ -236,17 +226,17 @@ class BuilderGenerator( /** * The core builder of the inner type. If the structure requires a fallible builder, this may use `?` to return - * errors + * errors. * ```rust * SomeStruct { - * field: builder.field, - * field2: builder.field2, - * field3: builder.field3.unwrap_or_default() - * field4: builder.field4.ok_or("field4 is required when building SomeStruct")? + * field1: builder.field1, + * field2: builder.field2.unwrap_or_default() + * field3: builder.field3.ok_or("field3 is required when building SomeStruct")? * } * ``` */ - protected fun coreBuilder(writer: RustWriter) { + private fun coreBuilder(writer: RustWriter) { + // TODO Refactor like server writer.rustBlock("#T", structureSymbol) { members.forEach { member -> val memberName = symbolProvider.toMemberName(member) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index e5c3de996d..b0473d9f93 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -157,7 +157,9 @@ class Instantiator( } else { writer.conditionalBlock( "Some(", ")", - conditional = ctx.builder || symbol.isOptional() +// conditional = ctx.builder || symbol.isOptional() + // TODO Client builder + conditional = symbol.isOptional() ) { writer.conditionalBlock( "Box::new(", @@ -261,16 +263,19 @@ class Instantiator( * MyStruct::builder().field_1("hello").field_2(5).build() * ``` */ + // TODO I wonder if we can create a renderStructure that does not use the builder. private fun renderStructure(writer: RustWriter, shape: StructureShape, data: ObjectNode, ctx: Ctx) { writer.write("#T::builder()", symbolProvider.toSymbol(shape)) data.members.forEach { (key, value) -> val memberShape = shape.expectMember(key.value) - writer.withBlock(".${memberShape.setterName()}(", ")") { + // TODO Client uses setter name. + writer.withBlock(".${symbolProvider.toMemberName(memberShape)}(", ")") { renderMember(this, memberShape, value, ctx) } } writer.write(".build()") - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { + // TODO Client + if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { writer.write(".unwrap()") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index bb4bab0dc6..21694a3cd2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.error.ErrorGenerator +import software.amazon.smithy.rust.codegen.smithy.isBoxed import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.renamedFrom import software.amazon.smithy.rust.codegen.smithy.rustType @@ -35,6 +36,7 @@ import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.getTrait import software.amazon.smithy.rust.codegen.util.hasTrait +import software.amazon.smithy.rust.codegen.util.toSnakeCase fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, block: RustWriter.() -> Unit) { rustBlock("impl ${symbolProvider.toSymbol(structureShape).name}") { @@ -50,6 +52,40 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } } +// TODO Perhaps move these into `StructureGenerator`? + +fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvider): Boolean { + val targetShape = model.expectShape(this.target) + + // TODO In reality, we need to recurse but not checking ourselves. + val symbol = symbolProvider.toSymbol(targetShape) + println(symbol.name) + if (symbol.name == "RecursiveShapesInputOutputNested1") { + return false + } + // TODO Why is the symbol not boxed? +// if (symbol.isBoxed()) { +// return false +// } + + return targetShape.isStructureShape + && StructureGenerator.serverHasFallibleBuilder(targetShape.asStructureShape().get(), model, symbolProvider) +} + +/** + * The name of the builder's setter the server deserializer should use. + * Setter names will never hit a reserved word and therefore never need escaping. + */ +fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: SymbolProvider): String { + val targetShape = model.expectShape(this.target) + if (targetShape.isStructureShape + && StructureGenerator.serverHasFallibleBuilder(targetShape.asStructureShape().get(), model, symbolProvider)) { + return "set_${this.memberName.toSnakeCase()}" + } + + return this.memberName.toSnakeCase() +} + class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, @@ -74,16 +110,23 @@ class StructureGenerator( companion object { /** Returns whether a structure shape requires a fallible builder to be generated. */ + // TODO Rename to `hasFallibleBuilder`. fun fallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = // All operation inputs should have fallible builders in case a new required field is added in the future. structureShape.hasTrait() || structureShape - .allMembers - .values.map { symbolProvider.toSymbol(it) }.any { + .members() + .map { symbolProvider.toSymbol(it) }.any { // If any members are not optional && we can't use a default, we need to - // generate a fallible builder + // generate a fallible builder. !it.isOptional() && !it.canUseDefault() } + + // TODO Ensure server subproject uses this function + // TODO Not quite right. @box not taken into account. Also shape builders / constrained shapes + fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider): Boolean = + structureShape.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } + || structureShape.members().any { it.targetNeedsValidation(model, symbolProvider) } } /** @@ -104,7 +147,8 @@ class StructureGenerator( } else "" } - /** Render a custom debug implementation + /** + * Render a custom debug implementation * When [SensitiveTrait] support is required, render a custom debug implementation to redact sensitive data */ private fun renderDebugImpl() { @@ -125,6 +169,18 @@ class StructureGenerator( } } + private fun renderValidateImpl() { + writer.rust( + """ + impl #T for $name { + type Unvalidated = #T; + } + """, + RuntimeType.ValidateTrait(), + shape.builderSymbol(symbolProvider) + ) + } + private fun renderStructureImpl() { if (accessorMembers.isEmpty()) { return @@ -170,6 +226,7 @@ class StructureGenerator( renderStructureImpl() renderDebugImpl() + renderValidateImpl() } private fun RustWriter.forEachMember( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index e3d0d56aaf..75d88276fd 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -39,8 +39,8 @@ import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant -import software.amazon.smithy.rust.codegen.smithy.generators.setterName import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation @@ -205,8 +205,18 @@ class JsonParserGenerator( rustBlock("match key.to_unescaped()?.as_ref()") { for (member in members) { rustBlock("${jsonName(member).dq()} =>") { - withBlock("builder = builder.${member.setterName()}(", ");") { + if (member.isOptional) { + withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider)}(", ");") { + deserializeMember(member) + } + } else { + rust("if let Some(v) = ") deserializeMember(member) + rust(""" + { + builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider)}(v); + } + """) } } } @@ -353,28 +363,42 @@ class JsonParserGenerator( private fun RustWriter.deserializeStruct(shape: StructureShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) + val returnBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) + val returnType = if (returnBuilder) { + shape.builderSymbol(symbolProvider) + } else { + symbol + } val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ - pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "Shape" to symbol, + "ReturnType" to returnType, *codegenScope, ) { startObjectOrNull { Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{Shape}::builder();", *codegenScope, "Shape" to symbol) deserializeStructInner(shape.members()) - withBlock("Ok(Some(builder.build()", "))") { - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { - rustTemplate( - """.map_err(|err| #{Error}::new( - #{ErrorReason}::Custom(format!("{}", err).into()), None) - )?""", - *codegenScope - ) - } + // Only call `build()` if the builder is not fallible. Otherwise, return the builder. + if (returnBuilder) { + rust("Ok(Some(builder))") + } else { + rust("Ok(Some(builder.build()))") +// withBlock("Ok(Some(builder.build()", "))") { +// if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { +// rustTemplate( +// """ +// .map_err(|err| #{Error}::new( +// #{ErrorReason}::Custom(format!("{}", err).into()), None) +// )? +// """, +// *codegenScope +// ) +// } +// } } } } diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 7de0872b71..3cf209afb6 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -185,6 +185,9 @@ pub enum RequestRejection { // error, but it would be a lot of effort for comparatively low benefit. /// Used when consuming the input struct builder. Build(crate::Error), + + // TODO Use the above one. + BuildV2(Box), } impl std::error::Error for RequestRejection {} diff --git a/rust-runtime/aws-smithy-http-server/src/validation.rs b/rust-runtime/aws-smithy-http-server/src/validation.rs new file mode 100644 index 0000000000..7f09f0b68d --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/validation.rs @@ -0,0 +1,7 @@ +pub trait Validate { + type Unvalidated; +} +pub enum Validated { + Validated(T), + Unvalidated(T::Unvalidated), +} diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index 492e2e7388..9ff1aa8cfe 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -15,6 +15,8 @@ mod rest_xml_unwrapped_errors; mod rest_xml_wrapped_errors; #[allow(unused)] mod server_operation_handler_trait; +#[allow(unused)] +mod validation; // This test is outside of uuid.rs to enable copying the entirety of uuid.rs into the SDK without // requiring a proptest dependency diff --git a/rust-runtime/inlineable/src/validation.rs b/rust-runtime/inlineable/src/validation.rs new file mode 100644 index 0000000000..49706de93e --- /dev/null +++ b/rust-runtime/inlineable/src/validation.rs @@ -0,0 +1,10 @@ +pub trait Validate { + type Unvalidated; +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum Validated { + Validated(T), + Unvalidated(T::Unvalidated), +} From 67e22ea83f4fab3827675a38740258277cbe816a Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 5 Apr 2022 19:20:15 +0200 Subject: [PATCH 005/255] Ignore Option failing tests --- .../generators/protocol/ServerProtocolTestGenerator.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 75a3675a57..d32d797aae 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -660,6 +660,12 @@ class ServerProtocolTestGenerator( FailingTest(RestJson, "RestJsonHttpWithEmptyStructurePayload", TestType.Request), FailingTest(RestJson, "RestJsonHttpResponseCodeDefaultsToModeledCode", TestType.Response), + // TODO(https://github.com/awslabs/smithy-rs/issues/1302) + FailingTest(RestJson, "RestJsonStreamingTraitsWithNoBlobBody", TestType.Request), + FailingTest(RestJson, "RestJsonStreamingTraitsWithNoBlobBody", TestType.Response), + FailingTest(RestJson, "RestJsonStreamingTraitsRequireLengthWithNoBlobBody", TestType.Request), + FailingTest(RestJson, "RestJsonStreamingTraitsRequireLengthWithNoBlobBody", TestType.Response), + FailingTest(RestJson, "RestJsonWithBodyExpectsApplicationJsonAccept", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsImpliedAccept", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonWithPayloadExpectsModeledAccept", TestType.MalformedRequest), From c55b259df25d03285f692ca9e1e15ed51d0e206c Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 6 Apr 2022 16:01:22 +0200 Subject: [PATCH 006/255] First round of cleanup --- codegen-server-test/model/simple.smithy | 26 +- .../generators/ServerBuilderGenerator.kt | 288 ++++++++---------- .../smithy/generators/StructureGenerator.kt | 25 +- 3 files changed, 178 insertions(+), 161 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 3192726153..f50e9283a6 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -14,6 +14,8 @@ service SimpleService { @http(uri: "/operation", method: "GET") operation AnOperation { + // input: RecursiveShapesInputOutput, + // output: RecursiveShapesInputOutput, input: AnOperationInput, output: AnOperationOutput, } @@ -31,7 +33,9 @@ structure ConA { @required conB: ConB, - optConB: ConB + optConB: ConB, + + // conBList: ConBList } structure ConB { @@ -43,3 +47,23 @@ structure ConB { optNice: String, optInt: Integer } + +// structure RecursiveShapesInputOutput { +// nested: RecursiveShapesInputOutputNested1 +// } +// +// structure RecursiveShapesInputOutputNested1 { +// foo: String, +// nested: RecursiveShapesInputOutputNested2 +// } +// +// structure RecursiveShapesInputOutputNested2 { +// bar: StringList, +// recursiveMember: RecursiveShapesInputOutputNested1, +// } +// + + +// list ConBList { +// member: ConB +// } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 1441ebb6f7..13b01f638f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.Attribute -import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock @@ -24,40 +23,24 @@ import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.targetNeedsValidation import software.amazon.smithy.rust.codegen.smithy.isOptional +import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.wrapValidated -import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -import java.util.* - -// TODO This function is the same as `BuilderGenerator.kt.` -fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): RuntimeType { - val symbol = symbolProvider.toSymbol(this) - val builderNamespace = RustReservedWords.escapeIfNeeded(symbol.name.toSnakeCase()) - return RuntimeType("Builder", null, "${symbol.namespace}::$builderNamespace") -} - -fun RuntimeConfig.operationBuildError() = RuntimeType.operationModule(this).member("BuildError") -fun RuntimeConfig.serializationError() = RuntimeType.operationModule(this).member("SerializationError") - -class OperationBuildError(private val runtimeConfig: RuntimeConfig) { - fun missingField(w: RustWriter, field: String, details: String) = "${w.format(runtimeConfig.operationBuildError())}::MissingField { field: ${field.dq()}, details: ${details.dq()} }" - fun invalidField(w: RustWriter, field: String, details: String) = "${w.format(runtimeConfig.operationBuildError())}::InvalidField { field: ${field.dq()}, details: ${details.dq()}.to_string() }" - fun serializationError(w: RustWriter, error: String) = "${w.format(runtimeConfig.operationBuildError())}::SerializationError($error.into())" -} // TODO Document differences: // - This one takes in codegenContext. +// - Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. +// - This builder is not `PartialEq`. class ServerBuilderGenerator( private val codegenContext: CodegenContext, private val shape: StructureShape @@ -65,7 +48,6 @@ class ServerBuilderGenerator( private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val members: List = shape.allMembers.values.toList() - // TODO Ensure everyone uses this instead of recomputing private val structureSymbol = symbolProvider.toSymbol(shape) private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() @@ -74,59 +56,9 @@ class ServerBuilderGenerator( writer.withModule(moduleName) { renderBuilder(this) } - - // TODO Can't we move these into the builder module? - if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { - renderTryFromBuilderImpl(writer) - } else { - renderFromBuilderImpl(writer) - } - } - - // TODO This impl does not take into account sensitive trait. - private fun renderImplDisplayValidationFailure(writer: RustWriter) { - writer.rustBlock("impl std::fmt::Display for ValidationFailure") { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - rustBlock("match self") { - validationFailures().forEach { - val arm = if (it.hasInner()) { - "ValidationFailure::${it.name()}(_)" - } else { - "ValidationFailure::${it.name()}" - } - rust("""$arm => write!(f, "${validationFailureErrorMessage(it)}"),""") - } - } - } - } - } - - // TODO This only needs to be generated for operation input shapes. - private fun renderImplFromValidationFailureForRequestRejection(writer: RustWriter) { - // TODO No need for rustBlock - writer.rustBlock("impl From for #T", ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig)) { - rustBlock("fn from(value: ValidationFailure) -> Self") { - rust("Self::BuildV2(value.into())") - } - } - } - - private fun renderImplFromBuilderForValidated(writer: RustWriter) { - // TODO No need for rustBlock - writer.rustBlock("impl From for #T", structureSymbol.wrapValidated()) { - rust( - """ - fn from(value: Builder) -> Self { - Self::Unvalidated(value) - } - """ - ) - } } private fun renderBuilder(writer: RustWriter) { - val builderName = "Builder" - if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { Attribute.Derives(setOf(RuntimeType.Debug)).render(writer) // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. @@ -137,57 +69,98 @@ class ServerBuilderGenerator( renderImplDisplayValidationFailure(writer) writer.rust("impl std::error::Error for ValidationFailure { }") + + // TODO This only needs to be generated for operation input shapes. renderImplFromValidationFailureForRequestRejection(writer) renderImplFromBuilderForValidated(writer) + + renderTryFromBuilderImpl(writer) + } else { + renderFromBuilderImpl(writer) } writer.docs("A builder for #D.", structureSymbol) - // Matching derives to the main structure + `Default` since we are a builder and everything is optional. + // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. // TODO Manually implement `Default` so that we can add custom docs. val baseDerives = structureSymbol.expectRustMetadata().derives - // TODO Document breaking `PartialEq`. val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) - writer.rustBlock("pub struct $builderName") { + writer.rustBlock("pub struct Builder") { for (member in members) { - // All fields in the builder are optional. val memberSymbol = builderMemberSymbol(member) val memberName = symbolProvider.toMemberName(member) renderBuilderMember(this, memberName, memberSymbol) } } - writer.rustBlock("impl $builderName") { + writer.rustBlock("impl Builder") { for (member in members) { renderBuilderMemberFn(this, member) if (member.targetNeedsValidation(model, symbolProvider)) { renderBuilderMemberSetterFn(this, member) } - - // Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. } renderBuildFn(this) } } + // TODO This impl does not take into account sensitive trait. + private fun renderImplDisplayValidationFailure(writer: RustWriter) { + writer.rustBlock("impl std::fmt::Display for ValidationFailure") { + rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("match self") { + validationFailures().forEach { + val arm = if (it.hasInner()) { + "ValidationFailure::${it.name()}(_)" + } else { + "ValidationFailure::${it.name()}" + } + rust("""$arm => write!(f, "${validationFailureErrorMessage(it)}"),""") + } + } + } + } + } + + private fun renderImplFromValidationFailureForRequestRejection(writer: RustWriter) { + writer.rustTemplate( + """ + impl From for #{RequestRejection} { + fn from(value: ValidationFailure) -> Self { + Self::BuildV2(value.into()) + } + } + """, + "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig) + ) + } + + private fun renderImplFromBuilderForValidated(writer: RustWriter) { + writer.rust( + """ + impl From for #{T} { + fn from(builder: Builder) -> Self { + Self::Unvalidated(builder) + } + } + """, + structureSymbol.wrapValidated() + ) + } + private fun renderBuildFn(implBlockWriter: RustWriter) { val fallibleBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) - val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { - true -> "Result<${implBlockWriter.format(outputSymbol)}, ValidationFailure>" - false -> implBlockWriter.format(outputSymbol) + true -> "Result<${implBlockWriter.format(structureSymbol)}, ValidationFailure>" + false -> implBlockWriter.format(structureSymbol) } // TODO Document when builder can fail. // TODO Document it returns the first error. - implBlockWriter.docs("Consumes the builder and constructs a #D.", outputSymbol) + implBlockWriter.docs("Consumes the builder and constructs a #D.", structureSymbol) implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { -// if (members.any { targetNeedsValidation(it) }) { -// implBlockWriter.rust("use std::convert::TryInto;") -// } conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { - // If a wrapper is specified, use the `::new` associated function to construct the wrapper. coreBuilder(this) } } @@ -208,6 +181,10 @@ class ServerBuilderGenerator( writer.write("pub(crate) $memberName: #T,", memberSymbol) } + /** + * Render a `foo` method for to set shape member `foo`. The caller must provide a value with the exact same type + * as the shape member's type. + */ private fun renderBuilderMemberFn( writer: RustWriter, member: MemberShape, @@ -235,8 +212,7 @@ class ServerBuilderGenerator( } } - - /* + /** * Render a `set_foo` method. This method is able to take in builders of structure shape types. */ private fun renderBuilderMemberSetterFn( @@ -246,18 +222,12 @@ class ServerBuilderGenerator( check(model.expectShape(member.target, StructureShape::class.java) != null) val builderMemberSymbol = builderMemberSymbol(member) - val inputType = builderMemberSymbol.rustType().stripOuter().implInto().let { - if (member.isOptional) { - "Option<$it>" - } else { - it - } - } + val inputType = builderMemberSymbol.rustType().stripOuter().implInto().letIf(member.isOptional) { "Option<$it>" } val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) // TODO: This method is only used by deserializers, so it will remain unused for shapes that are not (transitively) - // part of an operation input. We therefore `[allow(dead_code)]` here. + // part of an operation input. We therefore `[allow(dead_code)]` here. Attribute.AllowDeadCode.render(writer) // TODO(): `pub(crate)` until we commit to making builders of builders public. // Setter names will never hit a reserved word and therefore never need escaping. @@ -277,17 +247,25 @@ class ServerBuilderGenerator( } } - // TODO Docs + /** + * The kinds of validation failures that can occur when building the builder. + */ enum class ValidationFailureKind { + // A field is required but was not provided. MISSING_MEMBER, + // A builder was provided for a field targeting a struct, but that builder failed to build. BUILDER_FAILURE, } + data class ValidationFailure(val forMember: MemberShape, val kind: ValidationFailureKind) { fun name() = when (kind) { ValidationFailureKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" ValidationFailureKind.BUILDER_FAILURE -> "${forMember.memberName.toPascalCase()}ValidationFailure" } + /** + * Whether the validation failure is a Rust tuple struct with one element. + */ fun hasInner() = kind == ValidationFailureKind.BUILDER_FAILURE } @@ -311,14 +289,6 @@ class ServerBuilderGenerator( } } - // ONLY RETURNS BUILDER validation failure, intentional - private fun builderValidationFailureForMember(member: MemberShape) = - if (model.expectShape(member.target).isStructureShape) { - Optional.of(ValidationFailure(member, ValidationFailureKind.BUILDER_FAILURE)) - } else { - Optional.empty() - } - private fun validationFailureErrorMessage(validationFailure: ValidationFailure): String { val memberName = symbolProvider.toMemberName(validationFailure.forMember) // TODO $structureSymbol here is not quite right because it's printing the full namespace: crate:: in the context of the user will surely be different. @@ -330,81 +300,96 @@ class ServerBuilderGenerator( } private fun validationFailures() = members.flatMap { member -> - val ret = mutableListOf() - if (mustProvideValueForMember(member)) { - ret.add(ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER)) - } - val builderValidationFailure = builderValidationFailureForMember(member) - if (builderValidationFailure.isPresent) { - ret.add(builderValidationFailure.get()) - } + listOfNotNull( + builderMissingFieldForMember(member), + builderValidationFailureForMember(member), + ) + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constrained shapes. + } - // TODO Constrained shapes. + /** + * Returns the builder failure associated with the `member` field if its target requires validation. + */ + private fun builderValidationFailureForMember(member: MemberShape) = + if (member.targetNeedsValidation(model, symbolProvider)) { + ValidationFailure(member, ValidationFailureKind.BUILDER_FAILURE) + } else { + null + } - ret - } + /** + * Returns the builder failure associated with the `member` field if it is `required`. + */ + private fun builderMissingFieldForMember(member: MemberShape) = + if (symbolProvider.toSymbol(member).isOptional()) { + null + } else { + ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER) + } private fun renderTryFromBuilderImpl(writer: RustWriter) { - val shapeSymbol = symbolProvider.toSymbol(shape) - val builderSymbol = shape.builderSymbol(symbolProvider) + // TODO `TryFrom` is in Rust 2021's prelude. writer.rustTemplate( """ - impl std::convert::TryFrom<#{Builder}> for #{Shape} { - type Error = $moduleName::ValidationFailure; + impl std::convert::TryFrom for #{Structure} { + type Error = ValidationFailure; - fn try_from(value: #{Builder}) -> Result { - value.build() + fn try_from(builder: Builder) -> Result { + builder.build() } } """, - "Builder" to builderSymbol, - "Shape" to shapeSymbol, + "Structure" to structureSymbol, ) } private fun renderFromBuilderImpl(writer: RustWriter) { - val shapeSymbol = symbolProvider.toSymbol(shape) - val builderSymbol = shape.builderSymbol(symbolProvider) writer.rustTemplate( """ - impl From<#{Builder}> for #{Shape} { - fn from(value: #{Builder}) -> Self { - value.build() + impl From for #{Structure} { + fn from(builder: Builder) -> Self { + builder.build() } } """, - "Builder" to builderSymbol, - "Shape" to shapeSymbol, + "Structure" to structureSymbol, ) } - // TODO Docs + /** + * Returns the symbol for a builder's member. + * All builder members are optional, but only some are `Option`s where `T` needs to be validated. + */ private fun builderMemberSymbol(member: MemberShape): Symbol = symbolProvider.toSymbol(member) + // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } - .let { - if (member.targetNeedsValidation(model, symbolProvider)) it.wrapValidated() - else it - }.makeOptional() + // Wrap the symbol with the Cow-like `validation::Validated` type in case the target member shape needs validation. + .letIf(member.targetNeedsValidation(model, symbolProvider)) { it.wrapValidated() } + // Ensure we end up with an `Option`. + .makeOptional() /** - * TODO DOCS + * Writes the code to instantiate the struct the builder builds. + * + * Builder member types are either: + * 1. `Option>`; or + * 2. `Option`. + * + * The structs they build have member types: + * a) `Option`; or + * b) `T`. + * + * For each member, this function first unwraps case 1. into 2., and then converts into b) if necessary. */ private fun coreBuilder(writer: RustWriter) { - // Builder type: - // Option> - // Option - // - // Struct type: - // Option - // T writer.rustBlock("#T", structureSymbol) { for (member in members) { val memberName = symbolProvider.toMemberName(member) withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). - if (member.targetNeedsValidation(model, symbolProvider)) { + builderValidationFailureForMember(member)?.let { // TODO Remove `TryInto` import when we switch to 2021 edition. rustTemplate( """ @@ -415,22 +400,17 @@ class ServerBuilderGenerator( x.try_into() } }) - .map(|v| v.map_err(|err| ValidationFailure::${builderValidationFailureForMember(member).get().name()}(err))) + .map(|v| v.map_err(|err| ValidationFailure::${it.name()}(err))) .transpose()? """, "Validated" to RuntimeType.Validated() ) } - if (mustProvideValueForMember(member)) { - rust(".ok_or(ValidationFailure::${ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER).name()})?") + builderMissingFieldForMember(member)?.let { + rust(".ok_or(ValidationFailure::${it.name()})?") } } } } } - - // TODO We could move to extension function in `StructureGenerator.kt`. - // TODO Is it.isOptional() necessary? Won't canUseDefault take into account `Option`s already? - private fun mustProvideValueForMember(member: MemberShape) = - !symbolProvider.toSymbol(member).isOptional() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 21694a3cd2..3180b25758 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -28,7 +28,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.error.ErrorGenerator -import software.amazon.smithy.rust.codegen.smithy.isBoxed import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.renamedFrom import software.amazon.smithy.rust.codegen.smithy.rustType @@ -57,15 +56,29 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvider): Boolean { val targetShape = model.expectShape(this.target) - // TODO In reality, we need to recurse but not checking ourselves. +// val memberSymbol = symbolProvider.toSymbol(this) +// val targetSymbol = symbolProvider.toSymbol(targetShape) +// val hasRustBoxTrait = this.hasTrait() +// println("hasRustBoxTrait = $hasRustBoxTrait") +// println(symbol.name) + + // TODO We have a problem with recursion. In the case of recursive shapes whose members are all not `@required`, this + // function recurses indefinitely, but it should return the current member _does not_ require validation. + // We can detect recursion by checking the `RustBoxTrait` on the member, but we can't inspect the cycle. + // An easy way to determine whether a shape member requires validation would be if we defined the "closure of a shape `S`" + // as all the shapes reachable from `S`. Then a member shape whose target is a structure shape `S` requires + // validation if and only if any of the shapes in its closure is `@required` (or is a constrained shape, when we implement those). + // + // I see `TopologicalIndex.java` in Smithy allows me to get the _recursive_ closure of a shape, but I need its entire + // closure. + // Perhaps I can simply do `PathFinder.search(shape, "")` with an empty selector. val symbol = symbolProvider.toSymbol(targetShape) - println(symbol.name) if (symbol.name == "RecursiveShapesInputOutputNested1") { return false } - // TODO Why is the symbol not boxed? -// if (symbol.isBoxed()) { -// return false + +// if (targetShape.isListShape) { +// targetShape.asListShape().get() // } return targetShape.isStructureShape From e70ec64a2aa7e441e900cbc87225fab15183196f Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 6 Apr 2022 17:42:03 +0200 Subject: [PATCH 007/255] Put implementation of Validate trait inside builder module --- .../smithy/generators/ServerBuilderGenerator.kt | 13 +++++++++++++ .../codegen/smithy/generators/StructureGenerator.kt | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 13b01f638f..74022478df 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -79,6 +79,7 @@ class ServerBuilderGenerator( } else { renderFromBuilderImpl(writer) } + renderValidateImpl(writer) writer.docs("A builder for #D.", structureSymbol) // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. @@ -106,6 +107,18 @@ class ServerBuilderGenerator( } } + private fun renderValidateImpl(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{ValidateTrait} for #{Structure} { + type Unvalidated = Builder; + } + """, + "ValidateTrait" to RuntimeType.ValidateTrait(), + "Structure" to structureSymbol, + ) + } + // TODO This impl does not take into account sensitive trait. private fun renderImplDisplayValidationFailure(writer: RustWriter) { writer.rustBlock("impl std::fmt::Display for ValidationFailure") { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 3180b25758..3de2b2b1a7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -182,18 +182,6 @@ class StructureGenerator( } } - private fun renderValidateImpl() { - writer.rust( - """ - impl #T for $name { - type Unvalidated = #T; - } - """, - RuntimeType.ValidateTrait(), - shape.builderSymbol(symbolProvider) - ) - } - private fun renderStructureImpl() { if (accessorMembers.isEmpty()) { return @@ -239,7 +227,6 @@ class StructureGenerator( renderStructureImpl() renderDebugImpl() - renderValidateImpl() } private fun RustWriter.forEachMember( From 9e1bc4188de7ce982a2b2a5174c58cac6832b0d3 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 6 Apr 2022 18:36:57 +0200 Subject: [PATCH 008/255] Revert "Put implementation of Validate trait inside builder module" This reverts commit d94246b5df4d06a58cbcfb4ec3d10f66faeac1f6. --- .../smithy/generators/ServerBuilderGenerator.kt | 13 ------------- .../codegen/smithy/generators/StructureGenerator.kt | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 74022478df..13b01f638f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -79,7 +79,6 @@ class ServerBuilderGenerator( } else { renderFromBuilderImpl(writer) } - renderValidateImpl(writer) writer.docs("A builder for #D.", structureSymbol) // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. @@ -107,18 +106,6 @@ class ServerBuilderGenerator( } } - private fun renderValidateImpl(writer: RustWriter) { - writer.rustTemplate( - """ - impl #{ValidateTrait} for #{Structure} { - type Unvalidated = Builder; - } - """, - "ValidateTrait" to RuntimeType.ValidateTrait(), - "Structure" to structureSymbol, - ) - } - // TODO This impl does not take into account sensitive trait. private fun renderImplDisplayValidationFailure(writer: RustWriter) { writer.rustBlock("impl std::fmt::Display for ValidationFailure") { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 3de2b2b1a7..3180b25758 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -182,6 +182,18 @@ class StructureGenerator( } } + private fun renderValidateImpl() { + writer.rust( + """ + impl #T for $name { + type Unvalidated = #T; + } + """, + RuntimeType.ValidateTrait(), + shape.builderSymbol(symbolProvider) + ) + } + private fun renderStructureImpl() { if (accessorMembers.isEmpty()) { return @@ -227,6 +239,7 @@ class StructureGenerator( renderStructureImpl() renderDebugImpl() + renderValidateImpl() } private fun RustWriter.forEachMember( From 5b0da33ccbf9805541d509df1abb0fcd4737f638 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 7 Apr 2022 12:37:04 +0200 Subject: [PATCH 009/255] Abusing useShapeWriter to write ConstrainedListGenerator --- codegen-server-test/model/simple.smithy | 8 ++-- .../server/smithy/ServerCodegenVisitor.kt | 12 ++++++ .../generators/ConstrainedListGenerator.kt | 41 +++++++++++++++++++ .../rust/codegen/smithy/SymbolVisitor.kt | 29 ++++++++++--- .../smithy/generators/StructureGenerator.kt | 3 ++ 5 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index f50e9283a6..598d846b23 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -35,7 +35,7 @@ structure ConA { optConB: ConB, - // conBList: ConBList + conBList: ConBList } structure ConB { @@ -64,6 +64,6 @@ structure ConB { // -// list ConBList { -// member: ConB -// } +list ConBList { + member: ConB +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 1cfa6445a3..1f22289586 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeVisitor @@ -16,6 +17,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader @@ -174,6 +176,16 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } + override fun listShape(shape: ListShape) { + logger.info("[rust-server-codegen] Generating a wrapper tuple struct for list $shape") + + // TODO We only have to generate this for lists that are reachable from operation input AND from which you can + // reach a structure that requires validation e.g. Vec, Vec>... + rustCrate.useShapeWriter(shape) { writer -> + ConstrainedListGenerator(model, symbolProvider, writer, shape).render() + } + } + /** * String Shape Visitor * diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt new file mode 100644 index 0000000000..d94321d611 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider + +class ConstrainedListGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + val writer: RustWriter, + val shape: ListShape +) { + private val symbol = symbolProvider.toSymbol(shape) + private val name = "${shape.id.name}Wrapper" + + fun render() { + // TODO NO! Implement it for the vector type directly, no need for wrapper type. + // TODO Unit test that this is pub(crate). + writer.rustTemplate( + """ + pub(crate) struct $name(#{UnvalidatedList}); + + impl #{ValidateTrait} for $name { + type Unvalidated = #{UnvalidatedList}; + } + """, + "ValidateTrait" to RuntimeType.ValidateTrait(), + // TODO Note that when we have constraint shapes, this symbol will be incorrect; we need the corresponding + // "unconstrained" symbol. + "UnvalidatedList" to symbol, + ) + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 370b37409e..b6a1c6222b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -130,11 +130,22 @@ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { /** Set the symbolLocation for this symbol builder */ fun Symbol.Builder.locatedIn(symbolLocation: SymbolLocation): Symbol.Builder { val currentRustType = this.build().rustType() - check(currentRustType is RustType.Opaque) { "Only Opaque can have their namespace updated" } - val newRustType = currentRustType.copy(namespace = "crate::${symbolLocation.namespace}") - return this.definitionFile("src/${symbolLocation.filename}") - .namespace("crate::${symbolLocation.namespace}", "::") - .rustType(newRustType) + check(currentRustType is RustType.Opaque || currentRustType is RustType.Vec) { + "Only `Opaque` and `Vec` can have their namespace updated" + } + if (currentRustType is RustType.Opaque) { + val newRustType = currentRustType.copy(namespace = "crate::${symbolLocation.namespace}") + return this.definitionFile("src/${symbolLocation.filename}") + .namespace("crate::${symbolLocation.namespace}", "::") + .rustType(newRustType) + } else { + // TODO This is definitely abusing this. It's saying that the list shape is codegenned in models.rs + // but its namespace is the std::vec:: . It's better that I look for a way to write to models.rs (or somewhere else) + // directly e.g. rustCrate.withModule, rustCrate.withFile. + return this.definitionFile("src/${symbolLocation.filename}") + .namespace(symbolLocation.namespace, "::") + .rustType(currentRustType) + } } /** @@ -263,7 +274,13 @@ class SymbolVisitor( override fun listShape(shape: ListShape): Symbol { val inner = this.toSymbol(shape.member) - return symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() + val builder = symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner) + if (false) { + // TODO Only constrained lists should be located in models; we don't codegen anything for regular lists. + return builder.build() + } else { + return builder.locatedIn(Models).build() + } } override fun setShape(shape: SetShape): Symbol { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 3180b25758..06b570c00b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -239,6 +239,9 @@ class StructureGenerator( renderStructureImpl() renderDebugImpl() + + // TODO This only needs to be called in the server, for structures that are reachable from operation input AND + // that require validation. It's probably best that we move it to an entirely different class. renderValidateImpl() } From ab9858e176a7fa4098675934a3f6bece84321798 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 7 Apr 2022 16:16:16 +0200 Subject: [PATCH 010/255] save work, unstage --- .../server/smithy/ServerCodegenVisitor.kt | 8 +-- .../generators/ConstrainedListGenerator.kt | 53 +++++++++++++++++-- .../rust/codegen/smithy/SymbolVisitor.kt | 29 +++------- 3 files changed, 61 insertions(+), 29 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 1f22289586..50bf5c98ca 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator @@ -179,9 +180,10 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: override fun listShape(shape: ListShape) { logger.info("[rust-server-codegen] Generating a wrapper tuple struct for list $shape") - // TODO We only have to generate this for lists that are reachable from operation input AND from which you can - // reach a structure that requires validation e.g. Vec, Vec>... - rustCrate.useShapeWriter(shape) { writer -> + // TODO We only have to generate this for lists that are members of a structure that is reachable from operation + // input AND from which you can reach a structure that requires validation e.g. Vec, + // Vec>... + rustCrate.withModule(RustModule.private("validation")) { writer -> ConstrainedListGenerator(model, symbolProvider, writer, shape).render() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index d94321d611..b3d89a94b8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -5,12 +5,18 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.mapRustType +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.shape class ConstrainedListGenerator( val model: Model, @@ -24,18 +30,57 @@ class ConstrainedListGenerator( fun render() { // TODO NO! Implement it for the vector type directly, no need for wrapper type. // TODO Unit test that this is pub(crate). + + val unvalidatedSymbol = mapStructToBuilderRecursively(symbol) + writer.rustTemplate( """ - pub(crate) struct $name(#{UnvalidatedList}); + impl #{ValidateTrait} for #{Shape} { + type Unvalidated = #{UnvalidatedSymbol}; + } - impl #{ValidateTrait} for $name { - type Unvalidated = #{UnvalidatedList}; + impl std::convert::TryFrom for #{Structure} { + type Error = ValidationFailure; + + fn try_from(builder: Builder) -> Result { + builder.build() + } } """, "ValidateTrait" to RuntimeType.ValidateTrait(), // TODO Note that when we have constraint shapes, this symbol will be incorrect; we need the corresponding // "unconstrained" symbol. - "UnvalidatedList" to symbol, + "Shape" to symbol, + "UnvalidatedSymbol" to unvalidatedSymbol, ) } + + private fun mapStructToBuilderRecursively(symbol: Symbol): Symbol { + // TODO I think this will fail for lists of hash maps. + check(symbol.references.size <= 1) + + val shape = symbol.shape() + + if (shape.isStructureShape) { + val builderSymbolBuilder = shape.asStructureShape().get().builderSymbol(symbolProvider).toSymbol().toBuilder() + check(symbol.references.isEmpty()) + return builderSymbolBuilder.build() + } else { + return if (symbol.references.isEmpty()) { + symbol + } else { + // TODO This will fail with maps + check(shape.isListShape) + + val newSymbol = mapStructToBuilderRecursively(symbol.references[0].symbol) + val newType = RustType.Vec(newSymbol.rustType()) + // TODO Not using `mapRustType` because it adds a reference to itself, which I don't udnerstand. + Symbol.builder() + .rustType(newType) + .addReference(newSymbol) + .name(newType.name) + .build() + } + } + } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index b6a1c6222b..2228e0100c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -130,22 +130,13 @@ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { /** Set the symbolLocation for this symbol builder */ fun Symbol.Builder.locatedIn(symbolLocation: SymbolLocation): Symbol.Builder { val currentRustType = this.build().rustType() - check(currentRustType is RustType.Opaque || currentRustType is RustType.Vec) { - "Only `Opaque` and `Vec` can have their namespace updated" - } - if (currentRustType is RustType.Opaque) { - val newRustType = currentRustType.copy(namespace = "crate::${symbolLocation.namespace}") - return this.definitionFile("src/${symbolLocation.filename}") - .namespace("crate::${symbolLocation.namespace}", "::") - .rustType(newRustType) - } else { - // TODO This is definitely abusing this. It's saying that the list shape is codegenned in models.rs - // but its namespace is the std::vec:: . It's better that I look for a way to write to models.rs (or somewhere else) - // directly e.g. rustCrate.withModule, rustCrate.withFile. - return this.definitionFile("src/${symbolLocation.filename}") - .namespace(symbolLocation.namespace, "::") - .rustType(currentRustType) + check(currentRustType is RustType.Opaque) { + "Only `Opaque` can have their namespace updated" } + val newRustType = currentRustType.copy(namespace = "crate::${symbolLocation.namespace}") + return this.definitionFile("src/${symbolLocation.filename}") + .namespace("crate::${symbolLocation.namespace}", "::") + .rustType(newRustType) } /** @@ -274,13 +265,7 @@ class SymbolVisitor( override fun listShape(shape: ListShape): Symbol { val inner = this.toSymbol(shape.member) - val builder = symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner) - if (false) { - // TODO Only constrained lists should be located in models; we don't codegen anything for regular lists. - return builder.build() - } else { - return builder.locatedIn(Models).build() - } + return symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() } override fun setShape(shape: SetShape): Symbol { From 9bb19ae5a1ed6b7b74b827e995883de16058d16f Mon Sep 17 00:00:00 2001 From: david-perez Date: Sun, 10 Apr 2022 00:30:21 +0200 Subject: [PATCH 011/255] Looks like unconstrained list shapes structs are generated OK, has unit tests --- codegen-server-test/model/simple.smithy | 4 + .../server/smithy/ServerCodegenVisitor.kt | 16 ++- .../UnconstrainedShapeSymbolProvider.kt | 64 +++++++++++ .../generators/ConstrainedListGenerator.kt | 78 ++++++++----- .../server/smithy/generators/Constraints.kt | 76 +++++++++++++ .../UnconstrainedShapeSymbolProviderTest.kt | 103 ++++++++++++++++++ .../rust/codegen/smithy/SymbolVisitor.kt | 20 ++-- 7 files changed, 320 insertions(+), 41 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 598d846b23..004674a935 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -65,5 +65,9 @@ structure ConB { list ConBList { + member: AnotherList +} + +list AnotherList { member: ConB } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 50bf5c98ca..5787dfb38d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode @@ -59,6 +60,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val settings = ServerRustSettings.from(context.model, context.settings) private val symbolProvider: RustSymbolProvider + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model @@ -88,6 +90,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: val baseProvider = RustCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) + unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, mode = CodegenMode.Server) @@ -178,13 +181,14 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun listShape(shape: ListShape) { - logger.info("[rust-server-codegen] Generating a wrapper tuple struct for list $shape") + // TODO We only have to generate this for lists that are reachable from operation + // input AND from which you can reach a shape that is constrained. + if (shape.canReachConstrainedShape(model)) { + logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") - // TODO We only have to generate this for lists that are members of a structure that is reachable from operation - // input AND from which you can reach a structure that requires validation e.g. Vec, - // Vec>... - rustCrate.withModule(RustModule.private("validation")) { writer -> - ConstrainedListGenerator(model, symbolProvider, writer, shape).render() + rustCrate.withModule(RustModule.private("validation")) { writer -> + ConstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, writer, shape).render() + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt new file mode 100644 index 0000000000..0239f7e4cf --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.server.smithy.generators.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.generators.isConstrained +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.Validation +import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.util.toPascalCase +import software.amazon.smithy.rust.codegen.util.toSnakeCase + +class UnconstrainedShapeSymbolProvider( + private val base: RustSymbolProvider, + private val model: Model, + private val serviceShape: ServiceShape, +) : WrappingSymbolProvider(base) { + override fun toSymbol(shape: Shape): Symbol = + when { + shape.isListShape -> { + val listShape = shape.asListShape().get() + check(listShape.canReachConstrainedShape(model)) + + if (listShape.isConstrained()) { + TODO("Constraint traits on list shapes are currently not implemented") + } else { + val name = "${listShape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" + val rustType = RustType.Opaque(name, namespace) + Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Validation.filename) + .build() + } + } + shape.isSetShape -> { + TODO() + } + shape.isMapShape -> { + TODO() + } + shape.isStructureShape -> { + val structureShape = shape.asStructureShape().get() + check(structureShape.canReachConstrainedShape(model)) + + structureShape.builderSymbol(base) + } + // TODO Simple shapes can have constraint traits. + else -> base.toSymbol(shape) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index b3d89a94b8..a3e3225dac 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -8,53 +8,79 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.shape +import software.amazon.smithy.rust.codegen.util.toSnakeCase +// TODO Rename to Unconstrained? class ConstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, val writer: RustWriter, val shape: ListShape ) { private val symbol = symbolProvider.toSymbol(shape) - private val name = "${shape.id.name}Wrapper" fun render() { - // TODO NO! Implement it for the vector type directly, no need for wrapper type. + check(shape.canReachConstrainedShape(model)) + // TODO Unit test that this is pub(crate). - val unvalidatedSymbol = mapStructToBuilderRecursively(symbol) + val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name + val innerSymbol = unconstrainedShapeSymbolProvider.toSymbol(model.expectShape(shape.member.target)) - writer.rustTemplate( - """ - impl #{ValidateTrait} for #{Shape} { - type Unvalidated = #{UnvalidatedSymbol}; - } - - impl std::convert::TryFrom for #{Structure} { - type Error = ValidationFailure; - - fn try_from(builder: Builder) -> Result { - builder.build() - } - } - """, - "ValidateTrait" to RuntimeType.ValidateTrait(), - // TODO Note that when we have constraint shapes, this symbol will be incorrect; we need the corresponding - // "unconstrained" symbol. - "Shape" to symbol, - "UnvalidatedSymbol" to unvalidatedSymbol, - ) + // impl #{ValidateTrait} for #{Shape} { + // type Unvalidated = #{UnvalidatedSymbol}; + // } + // + // impl std::convert::TryFrom for #{Structure} { + // type Error = ValidationFailure; + // + // fn try_from(builder: Builder) -> Result { + // builder.build() + // } + // } + + writer.withModule(module, RustMetadata(public = false)) { + rustTemplate( + """ + pub(crate) struct $name(#{UnconstrainedInnerSymbol}); + """, +// "ValidateTrait" to RuntimeType.ValidateTrait(), + "UnconstrainedInnerSymbol" to innerSymbol, +// "Shape" to symbol, +// "UnvalidatedSymbol" to unvalidatedSymbol, + ) + } } +// private fun unconstrainedSymbol(shape: ListShape): Symbol { +// mapStructToBuilderRecursively(symbol) +// } + +// private fun structDefinition() = if (shape.isConstrained()) { +// "pub ${name()}();" +// } else { +// "${name()};" +// } +// +// private fun name() = if (shape.isConstrained()) { +// shape.id.name +// } else { +// "${shape.id.name}Constrained" +// } + private fun mapStructToBuilderRecursively(symbol: Symbol): Symbol { // TODO I think this will fail for lists of hash maps. check(symbol.references.size <= 1) @@ -62,7 +88,7 @@ class ConstrainedListGenerator( val shape = symbol.shape() if (shape.isStructureShape) { - val builderSymbolBuilder = shape.asStructureShape().get().builderSymbol(symbolProvider).toSymbol().toBuilder() + val builderSymbolBuilder = shape.asStructureShape().get().builderSymbol(symbolProvider).toBuilder() check(symbol.references.isEmpty()) return builderSymbolBuilder.build() } else { @@ -74,7 +100,7 @@ class ConstrainedListGenerator( val newSymbol = mapStructToBuilderRecursively(symbol.references[0].symbol) val newType = RustType.Vec(newSymbol.rustType()) - // TODO Not using `mapRustType` because it adds a reference to itself, which I don't udnerstand. + // TODO Not using `mapRustType` because it adds a reference to itself, which is wrong. Symbol.builder() .rustType(newType) .addReference(newSymbol) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt new file mode 100644 index 0000000000..44ce6989d0 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt @@ -0,0 +1,76 @@ +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.SetShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.rust.codegen.util.hasTrait + +// TODO Find a better place for these primitives. Probably rust.codegen.util + +fun Shape.hasConstraintTrait() = + this.hasTrait() || + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() + +fun Shape.isConstrained() = when (this) { + is StructureShape -> { + this.members().any { it.isRequired } + } + else -> { + this.hasConstraintTrait() + } +} + +fun StructureShape.canReachConstrainedShape(model: Model): Boolean { + if (this.isConstrained()) { + return true + } + + return this.members().map { model.expectShape(it.target) }.any { + it.isConstrained() || unconstrainedShapeCanReachConstrainedShape(it, model) + } +} + +fun CollectionShape.canReachConstrainedShape(model: Model): Boolean { + if (this.isConstrained()) { + return true + } + + val member = model.expectShape(this.member.target) + + return member.isConstrained() || unconstrainedShapeCanReachConstrainedShape(member, model) +} + +fun ListShape.canReachConstrainedShape(model: Model) = (this as CollectionShape).canReachConstrainedShape(model) +fun SetShape.canReachConstrainedShape(model: Model) = (this as CollectionShape).canReachConstrainedShape(model) + +fun MapShape.canReachConstrainedShape(model: Model): Boolean { + if (this.isConstrained()) { + return true + } + + val key = model.expectShape(this.key.target) + val value = model.expectShape(this.key.target) + + return key.isConstrained() || value.isConstrained() || unconstrainedShapeCanReachConstrainedShape(value, model) +} + +private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model): Boolean { + check(!shape.isConstrained()) { "This function can only be called with unconstrained shapes" } + + return when (shape) { + is StructureShape -> shape.canReachConstrainedShape(model) + is CollectionShape -> shape.canReachConstrainedShape(model) + is MapShape -> shape.canReachConstrainedShape(model) + else -> false + } +} \ No newline at end of file diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt new file mode 100644 index 0000000000..de2a3779ed --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.util.lookup +import java.lang.IllegalStateException + +class UnconstrainedShapeSymbolProviderTest { + private val baseModelString = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + list: ListA + } + """ + + @Test + fun `it should adjust types for unconstrained shapes`() { + val model = + """ + $baseModelString + + list ListA { + member: ListB + } + + list ListB { + member: StructureC + } + + structure StructureC { + @required + string: String + } + """.asSmithyModel() + + val serviceShape = model.lookup("test#TestService") + val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) + + val listAShape = model.lookup("test#ListA") + val listAType = symbolProvider.toSymbol(listAShape).rustType() + + val listBShape = model.lookup("test#ListB") + val listBType = symbolProvider.toSymbol(listBShape).rustType() + + val structureCShape = model.lookup("test#StructureC") + val structureCType = symbolProvider.toSymbol(structureCShape).rustType() + + listAType shouldBe RustType.Opaque("ListAUnconstrained", "crate::validation::list_a_unconstrained") + listBType shouldBe RustType.Opaque("ListBUnconstrained", "crate::validation::list_b_unconstrained") + structureCType shouldBe RustType.Opaque("Builder", "crate::model::structure_c") + } + + @Test + fun `it should throw if called with a shape that cannot reach a constrained shape`() { + val model = + """ + $baseModelString + + list ListA { + member: StructureB + } + + structure StructureB { + string: String + } + """.asSmithyModel() + + val serviceShape = model.lookup("test#TestService") + val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) + + val listAShape = model.lookup("test#ListA") + + shouldThrow { + symbolProvider.toSymbol(listAShape) + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 2228e0100c..8ba7524913 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -90,6 +90,8 @@ val Operations = SymbolLocation("operation") val Serializers = SymbolLocation("serializer") val Inputs = SymbolLocation("input") val Outputs = SymbolLocation("output") +// TODO Rename to constraints? +val Validation = SymbolLocation("validation") /** * Make the Rust type of a symbol optional (hold `Option`) @@ -357,15 +359,15 @@ class SymbolVisitor( override fun timestampShape(shape: TimestampShape?): Symbol { return RuntimeType.DateTime(config.runtimeConfig).toSymbol() } +} - private fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder { - val builder = Symbol.builder().putProperty(SHAPE_KEY, shape) - return builder.rustType(rustType) - .name(rustType.name) - // Every symbol that actually gets defined somewhere should set a definition file - // If we ever generate a `thisisabug.rs`, there is a bug in our symbol generation - .definitionFile("thisisabug.rs") - } +fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder { + val builder = Symbol.builder().putProperty(SHAPE_KEY, shape) + return builder.rustType(rustType) + .name(rustType.name) + // Every symbol that actually gets defined somewhere should set a definition file + // If we ever generate a `thisisabug.rs`, there is a bug in our symbol generation + .definitionFile("thisisabug.rs") } // TODO(chore): Move this to a useful place @@ -416,7 +418,7 @@ fun Symbol.isOptional(): Boolean = when (this.rustType()) { fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter() is RustType.Box // Symbols should _always_ be created with a Rust type & shape attached -fun Symbol.rustType(): RustType = this.getProperty(RUST_TYPE_KEY, RustType::class.java).get() +fun Symbol.rustType(): RustType = this.expectProperty(RUST_TYPE_KEY, RustType::class.java) fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java) /** From 9aec33185871d161795fd1357fad9c68777190f4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Sun, 10 Apr 2022 19:59:15 +0200 Subject: [PATCH 012/255] Lists done, SST and SRT work without failures --- .../ConstraintViolationSymbolProvider.kt | 77 +++++++++++ .../server/smithy/ServerCodegenVisitor.kt | 9 +- .../generators/ConstrainedListGenerator.kt | 130 ++++++++---------- .../server/smithy/generators/Constraints.kt | 76 ---------- .../generators/ServerBuilderGenerator.kt | 43 +++--- .../ServerHttpBoundProtocolGenerator.kt | 2 +- .../UnconstrainedShapeSymbolProviderTest.kt | 4 +- .../smithy/rust/codegen/rustlang/RustTypes.kt | 10 +- .../smithy/rust/codegen/smithy/Constraints.kt | 86 ++++++++++++ .../UnconstrainedShapeSymbolProvider.kt | 14 +- .../smithy/generators/StructureGenerator.kt | 36 +++-- .../protocols/parse/JsonParserGenerator.kt | 39 ++++-- rust-runtime/inlineable/src/validation.rs | 4 +- 13 files changed, 312 insertions(+), 218 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt delete mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt rename {codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server => codegen/src/main/kotlin/software/amazon/smithy/rust/codegen}/smithy/UnconstrainedShapeSymbolProvider.kt (78%) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt new file mode 100644 index 0000000000..c5dbe00dd4 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -0,0 +1,77 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.Validation +import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.util.toPascalCase +import software.amazon.smithy.rust.codegen.util.toSnakeCase + +// TODO Unit tests. +class ConstraintViolationSymbolProvider( + private val base: RustSymbolProvider, + private val model: Model, + private val serviceShape: ServiceShape, +) : WrappingSymbolProvider(base) { + override fun toSymbol(shape: Shape): Symbol = + when { + shape.isListShape -> { + val listShape = shape.asListShape().get() + check(listShape.canReachConstrainedShape(model, base)) + + if (listShape.isConstrained(base)) { + TODO("Constraint traits on list shapes are currently not implemented") + } else { + val name = "ValidationFailure" + // TODO This name is common in `UnconstrainedShapeSymbolProvider`, extract somewhere. + val unconstrainedListName = "${listShape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedListName.toSnakeCase())}" + val rustType = RustType.Opaque(name, namespace) + Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Validation.filename) + .build() + } + } + shape.isSetShape -> { + TODO() + } + shape.isMapShape -> { + TODO() + } + shape.isStructureShape -> { + val structureShape = shape.asStructureShape().get() + check(structureShape.canReachConstrainedShape(model, base)) + + val builderSymbol = structureShape.builderSymbol(base) + + val name = "ValidationFailure" + val namespace = builderSymbol.namespace + val rustType = RustType.Opaque(name, namespace) + Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Validation.filename) + .build() + } + // TODO Simple shapes can have constraint traits. + else -> base.toSymbol(shape) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 5787dfb38d..e5b14ea862 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator @@ -61,6 +62,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val symbolProvider: RustSymbolProvider private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model @@ -91,6 +93,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, mode = CodegenMode.Server) @@ -183,11 +186,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: override fun listShape(shape: ListShape) { // TODO We only have to generate this for lists that are reachable from operation // input AND from which you can reach a shape that is constrained. - if (shape.canReachConstrainedShape(model)) { + if (shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") rustCrate.withModule(RustModule.private("validation")) { writer -> - ConstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, writer, shape).render() + ConstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index a3e3225dac..252d187478 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -5,108 +5,90 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.shape -import software.amazon.smithy.rust.codegen.util.toSnakeCase +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.wrapValidated // TODO Rename to Unconstrained? class ConstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: ListShape ) { - private val symbol = symbolProvider.toSymbol(shape) - fun render() { - check(shape.canReachConstrainedShape(model)) + check(shape.canReachConstrainedShape(model, symbolProvider)) // TODO Unit test that this is pub(crate). + // TODO Some of these can be come private properties. val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() val name = symbol.name - val innerSymbol = unconstrainedShapeSymbolProvider.toSymbol(model.expectShape(shape.member.target)) - - // impl #{ValidateTrait} for #{Shape} { - // type Unvalidated = #{UnvalidatedSymbol}; - // } - // - // impl std::convert::TryFrom for #{Structure} { - // type Error = ValidationFailure; - // - // fn try_from(builder: Builder) -> Result { - // builder.build() - // } - // } + val innerShape = model.expectShape(shape.member.target) + val innerSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) + // TODO: We will need a `ConstrainedSymbolProvider` when we have constraint traits. + val constrainedSymbol = symbolProvider.toSymbol(shape) + val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name + val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - writer.withModule(module, RustMetadata(public = false)) { + // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. + // TODO I don't understand why pub(crate) suffices, when we're leaking it via the server builder validation failure enum variant definition. + // TODO The implementation of the Validate trait is probably not for the correct type. There might be more than + // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint + // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. + // So we might have to newtype things. + writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { rustTemplate( """ - pub(crate) struct $name(#{UnconstrainedInnerSymbol}); + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) Vec<#{InnerUnconstrainedSymbol}>); + + impl #{ValidateTrait} for #{ConstrainedSymbol} { + type Unvalidated = $name; + } + + impl From<$name> for #{Validated} { + fn from(value: $name) -> Self { + Self::Unvalidated(value) + } + } + + ##[derive(Debug)] + pub struct $constraintViolationName(#{InnerConstraintViolationSymbol}); + + impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { + type Error = $constraintViolationName; + + fn try_from(value: $name) -> Result { + let res: Result = value + .0 + .into_iter() + .map(|inner| { + use std::convert::TryInto; + inner.try_into() + }) + .collect(); + res.map_err(|err| ValidationFailure(err)) + } + } """, -// "ValidateTrait" to RuntimeType.ValidateTrait(), - "UnconstrainedInnerSymbol" to innerSymbol, -// "Shape" to symbol, -// "UnvalidatedSymbol" to unvalidatedSymbol, + "InnerUnconstrainedSymbol" to innerSymbol, + "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, + "ConstrainedSymbol" to constrainedSymbol, + "Validated" to constrainedSymbol.wrapValidated(), + "ValidateTrait" to RuntimeType.ValidateTrait(), ) } } - -// private fun unconstrainedSymbol(shape: ListShape): Symbol { -// mapStructToBuilderRecursively(symbol) -// } - -// private fun structDefinition() = if (shape.isConstrained()) { -// "pub ${name()}();" -// } else { -// "${name()};" -// } -// -// private fun name() = if (shape.isConstrained()) { -// shape.id.name -// } else { -// "${shape.id.name}Constrained" -// } - - private fun mapStructToBuilderRecursively(symbol: Symbol): Symbol { - // TODO I think this will fail for lists of hash maps. - check(symbol.references.size <= 1) - - val shape = symbol.shape() - - if (shape.isStructureShape) { - val builderSymbolBuilder = shape.asStructureShape().get().builderSymbol(symbolProvider).toBuilder() - check(symbol.references.isEmpty()) - return builderSymbolBuilder.build() - } else { - return if (symbol.references.isEmpty()) { - symbol - } else { - // TODO This will fail with maps - check(shape.isListShape) - - val newSymbol = mapStructToBuilderRecursively(symbol.references[0].symbol) - val newType = RustType.Vec(newSymbol.rustType()) - // TODO Not using `mapRustType` because it adds a reference to itself, which is wrong. - Symbol.builder() - .rustType(newType) - .addReference(newSymbol) - .name(newType.name) - .build() - } - } - } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt deleted file mode 100644 index 44ce6989d0..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/Constraints.kt +++ /dev/null @@ -1,76 +0,0 @@ -package software.amazon.smithy.rust.codegen.server.smithy.generators - -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.SetShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.model.traits.PatternTrait -import software.amazon.smithy.model.traits.RangeTrait -import software.amazon.smithy.rust.codegen.util.hasTrait - -// TODO Find a better place for these primitives. Probably rust.codegen.util - -fun Shape.hasConstraintTrait() = - this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() - -fun Shape.isConstrained() = when (this) { - is StructureShape -> { - this.members().any { it.isRequired } - } - else -> { - this.hasConstraintTrait() - } -} - -fun StructureShape.canReachConstrainedShape(model: Model): Boolean { - if (this.isConstrained()) { - return true - } - - return this.members().map { model.expectShape(it.target) }.any { - it.isConstrained() || unconstrainedShapeCanReachConstrainedShape(it, model) - } -} - -fun CollectionShape.canReachConstrainedShape(model: Model): Boolean { - if (this.isConstrained()) { - return true - } - - val member = model.expectShape(this.member.target) - - return member.isConstrained() || unconstrainedShapeCanReachConstrainedShape(member, model) -} - -fun ListShape.canReachConstrainedShape(model: Model) = (this as CollectionShape).canReachConstrainedShape(model) -fun SetShape.canReachConstrainedShape(model: Model) = (this as CollectionShape).canReachConstrainedShape(model) - -fun MapShape.canReachConstrainedShape(model: Model): Boolean { - if (this.isConstrained()) { - return true - } - - val key = model.expectShape(this.key.target) - val value = model.expectShape(this.key.target) - - return key.isConstrained() || value.isConstrained() || unconstrainedShapeCanReachConstrainedShape(value, model) -} - -private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model): Boolean { - check(!shape.isConstrained()) { "This function can only be called with unconstrained shapes" } - - return when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model) - is CollectionShape -> shape.canReachConstrainedShape(model) - is MapShape -> shape.canReachConstrainedShape(model) - else -> false - } -} \ No newline at end of file diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 13b01f638f..92d851caa7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -47,6 +48,8 @@ class ServerBuilderGenerator( ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val constraintViolationSymbolProvider = + ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() @@ -87,11 +90,7 @@ class ServerBuilderGenerator( val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) writer.rustBlock("pub struct Builder") { - for (member in members) { - val memberSymbol = builderMemberSymbol(member) - val memberName = symbolProvider.toMemberName(member) - renderBuilderMember(this, memberName, memberSymbol) - } + members.forEach { renderBuilderMember(this, it) } } writer.rustBlock("impl Builder") { @@ -175,7 +174,9 @@ class ServerBuilderGenerator( } // TODO(EventStream): [DX] Consider updating builders to take EventInputStream as Into - private fun renderBuilderMember(writer: RustWriter, memberName: String, memberSymbol: Symbol) { + private fun renderBuilderMember(writer: RustWriter, member: MemberShape) { + val memberSymbol = builderMemberSymbol(member) + val memberName = symbolProvider.toMemberName(member) // Builder members are crate-public to enable using them directly in serializers/deserializers. // During XML deserialization, `builder..take` is used to append to lists and maps. writer.write("pub(crate) $memberName: #T,", memberSymbol) @@ -219,8 +220,6 @@ class ServerBuilderGenerator( writer: RustWriter, member: MemberShape, ) { - check(model.expectShape(member.target, StructureShape::class.java) != null) - val builderMemberSymbol = builderMemberSymbol(member) val inputType = builderMemberSymbol.rustType().stripOuter().implInto().letIf(member.isOptional) { "Option<$it>" } val memberName = symbolProvider.toMemberName(member) @@ -253,24 +252,24 @@ class ServerBuilderGenerator( enum class ValidationFailureKind { // A field is required but was not provided. MISSING_MEMBER, - // A builder was provided for a field targeting a struct, but that builder failed to build. - BUILDER_FAILURE, + // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. + CONSTRAINED_SHAPE_FAILURE, } data class ValidationFailure(val forMember: MemberShape, val kind: ValidationFailureKind) { fun name() = when (kind) { ValidationFailureKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ValidationFailureKind.BUILDER_FAILURE -> "${forMember.memberName.toPascalCase()}ValidationFailure" + ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ValidationFailure" } /** * Whether the validation failure is a Rust tuple struct with one element. */ - fun hasInner() = kind == ValidationFailureKind.BUILDER_FAILURE + fun hasInner() = kind == ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE } private fun renderValidationFailure(writer: RustWriter, validationFailure: ValidationFailure) { - if (validationFailure.kind == ValidationFailureKind.BUILDER_FAILURE) { + if (validationFailure.kind == ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE) { // TODO(): `#[doc(hidden)]` until we commit to making builders of builders public. Attribute.DocHidden.render(writer) } @@ -279,12 +278,11 @@ class ServerBuilderGenerator( when (validationFailure.kind) { ValidationFailureKind.MISSING_MEMBER -> writer.rust("${validationFailure.name()},") - ValidationFailureKind.BUILDER_FAILURE -> { - val targetStructureShape = model.expectShape(validationFailure.forMember.target, StructureShape::class.java) - writer.rust("${validationFailure.name()}(<#{T} as std::convert::TryFrom<#{T}>>::Error),", - symbolProvider.toSymbol(targetStructureShape), - targetStructureShape.builderSymbol(symbolProvider) - ) + ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> { + val targetShape = model.expectShape(validationFailure.forMember.target) + // Note we cannot express the inner validation failure as `>::Error`, because `T` might + // be `pub(crate)` and that would leak `T` in a public interface. + writer.rust("${validationFailure.name()}(#T),", constraintViolationSymbolProvider.toSymbol(targetShape)) } } } @@ -295,7 +293,7 @@ class ServerBuilderGenerator( return when (validationFailure.kind) { ValidationFailureKind.MISSING_MEMBER -> "`$memberName` was not specified but it is required when building `$structureSymbol`" // TODO Nest errors. - ValidationFailureKind.BUILDER_FAILURE -> "validation failure occurred when building member `$memberName`, when building `$structureSymbol`" + ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> "validation failure occurred building member `$memberName` when building `$structureSymbol`" } } @@ -304,7 +302,6 @@ class ServerBuilderGenerator( builderMissingFieldForMember(member), builderValidationFailureForMember(member), ) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constrained shapes. } /** @@ -312,7 +309,7 @@ class ServerBuilderGenerator( */ private fun builderValidationFailureForMember(member: MemberShape) = if (member.targetNeedsValidation(model, symbolProvider)) { - ValidationFailure(member, ValidationFailureKind.BUILDER_FAILURE) + ValidationFailure(member, ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE) } else { null } @@ -321,6 +318,8 @@ class ServerBuilderGenerator( * Returns the builder failure associated with the `member` field if it is `required`. */ private fun builderMissingFieldForMember(member: MemberShape) = + // TODO: We go through the symbol provider because non-`required` blob streaming members are interpreted as `required`, + // so we can't use `member.isOptional`. See https://github.com/awslabs/smithy-rs/issues/1302. if (symbolProvider.toSymbol(member).isOptional()) { null } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 49ae27a1ac..54f645daa6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -442,7 +442,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } val status = - variantShape.getTrait()?.let { trait -> trait.code } + variantShape.getTrait()?.code ?: errorTrait.defaultHttpStatusCode rustTemplate( """ diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index de2a3779ed..ed4b1837f9 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -10,10 +10,10 @@ import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup @@ -60,6 +60,8 @@ class UnconstrainedShapeSymbolProviderTest { """.asSmithyModel() val serviceShape = model.lookup("test#TestService") + // TODO I can't move this file to `codegen` subproject because `serverTestSymbolProvider` is only in server subproject, + // but I need the symbol provider to be in `codegen` subproject because it's used in the JsonParser. val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) val listAShape = model.lookup("test#ListA") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index b0cc8269aa..ec4384963b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -279,7 +279,9 @@ fun RustType.isCopy(): Boolean = when (this) { data class RustMetadata( val derives: Attribute.Derives = Attribute.Derives.Empty, val additionalAttributes: List = listOf(), - val public: Boolean + val public: Boolean, + // TODO Refactor using enum + val pubCrate: Boolean = false ) { fun withDerives(vararg newDerive: RuntimeType): RustMetadata = this.copy(derives = derives.copy(derives = derives.derives + newDerive)) @@ -296,8 +298,10 @@ data class RustMetadata( return this } - fun renderVisibility(writer: RustWriter): RustMetadata { - if (public) { + private fun renderVisibility(writer: RustWriter): RustMetadata { + if (pubCrate) { + writer.writeInline("pub (crate) ") + } else if (public) { writer.writeInline("pub ") } return this diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt new file mode 100644 index 0000000000..16aec21453 --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -0,0 +1,86 @@ +package software.amazon.smithy.rust.codegen.smithy + +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.SetShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.rust.codegen.util.hasTrait + +// TODO Find a better place for these primitives. Probably rust.codegen.util + +// TODO This will work fine if we include RequiredTrait too won't it? +fun Shape.hasConstraintTrait() = + this.hasTrait() || + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() + +fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { + is StructureShape -> { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The only reason why the functions in this file have + // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. + this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } + } + else -> { + this.hasConstraintTrait() + } +} + +fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { + if (this.isConstrained(symbolProvider)) { + return true + } + + return this.members().map { model.expectShape(it.target) }.any { + it.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(it, model, symbolProvider) + } +} + +fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { + if (this.isConstrained(symbolProvider)) { + return true + } + + val member = model.expectShape(this.member.target) + + return member.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(member, model, symbolProvider) +} + +fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) +fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) + +fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { + if (this.isConstrained(symbolProvider)) { + return true + } + + val key = model.expectShape(this.key.target) + val value = model.expectShape(this.key.target) + + return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider) +} + +private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model, symbolProvider: SymbolProvider): Boolean { + check(!shape.isConstrained(symbolProvider)) { "This function can only be called with unconstrained shapes" } + + // TODO + if (shape.id.name == "RecursiveShapesInputOutputNested1") { + return false + } + + return when (shape) { + is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) + is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) + is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + else -> false + } +} \ No newline at end of file diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt similarity index 78% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 0239f7e4cf..d96aab9ce3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.rust.codegen.server.smithy +package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model @@ -11,13 +11,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType -import software.amazon.smithy.rust.codegen.server.smithy.generators.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.server.smithy.generators.isConstrained -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.Validation -import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -30,9 +24,9 @@ class UnconstrainedShapeSymbolProvider( when { shape.isListShape -> { val listShape = shape.asListShape().get() - check(listShape.canReachConstrainedShape(model)) + check(listShape.canReachConstrainedShape(model, base)) - if (listShape.isConstrained()) { + if (listShape.isConstrained(base)) { TODO("Constraint traits on list shapes are currently not implemented") } else { val name = "${listShape.id.getName(serviceShape).toPascalCase()}Unconstrained" @@ -54,7 +48,7 @@ class UnconstrainedShapeSymbolProvider( } shape.isStructureShape -> { val structureShape = shape.asStructureShape().get() - check(structureShape.canReachConstrainedShape(model)) + check(structureShape.canReachConstrainedShape(model, base)) structureShape.builderSymbol(base) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 06b570c00b..b2a64e4de3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rust.codegen.smithy.generators 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.CollectionShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape @@ -25,6 +27,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.error.ErrorGenerator @@ -77,12 +80,13 @@ fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvid return false } -// if (targetShape.isListShape) { -// targetShape.asListShape().get() -// } - - return targetShape.isStructureShape - && StructureGenerator.serverHasFallibleBuilder(targetShape.asStructureShape().get(), model, symbolProvider) + return when { + targetShape.isListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) + targetShape.isSetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) + targetShape.isMapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) + targetShape.isStructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) + else -> false + } } /** @@ -90,13 +94,18 @@ fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvid * Setter names will never hit a reserved word and therefore never need escaping. */ fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: SymbolProvider): String { - val targetShape = model.expectShape(this.target) - if (targetShape.isStructureShape - && StructureGenerator.serverHasFallibleBuilder(targetShape.asStructureShape().get(), model, symbolProvider)) { - return "set_${this.memberName.toSnakeCase()}" + val canReachConstrainedShape = when (val targetShape = model.expectShape(this.target)) { + is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) + is MapShape -> targetShape.canReachConstrainedShape(model, symbolProvider) + is StructureShape -> targetShape.canReachConstrainedShape(model, symbolProvider) + else -> false } - return this.memberName.toSnakeCase() + return if (canReachConstrainedShape) { + "set_${this.memberName.toSnakeCase()}" + } else { + this.memberName.toSnakeCase() + } } class StructureGenerator( @@ -137,9 +146,8 @@ class StructureGenerator( // TODO Ensure server subproject uses this function // TODO Not quite right. @box not taken into account. Also shape builders / constrained shapes - fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider): Boolean = - structureShape.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } - || structureShape.members().any { it.targetNeedsValidation(model, symbolProvider) } + fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider) = + structureShape.canReachConstrainedShape(model, symbolProvider) } /** diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 75d88276fd..5a16d4295e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -35,8 +35,9 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.canUseDefault -import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName @@ -52,6 +53,9 @@ import software.amazon.smithy.rust.codegen.util.inputShape import software.amazon.smithy.rust.codegen.util.outputShape import software.amazon.smithy.utils.StringUtils +// TODO: Separate commit: Make all functions pub(crate). If the functions have in their type signature a pub(crate) type, +// and the function is declared `pub`, Rust will complain, even if the json_deser module is not `pub`. + class JsonParserGenerator( codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, @@ -60,6 +64,7 @@ class JsonParserGenerator( ) : StructuredDataParserGenerator { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, codegenContext.serviceShape) private val runtimeConfig = codegenContext.runtimeConfig private val mode = codegenContext.mode private val smithyJson = CargoDependency.smithyJson(runtimeConfig).asType() @@ -97,7 +102,7 @@ class JsonParserGenerator( return RuntimeType.forInlineFun(fnName, jsonDeserModule) { val unusedMut = if (includedMembers.isEmpty()) "##[allow(unused_mut)] " else "" it.rustBlockTemplate( - "pub fn $fnName(value: &[u8], ${unusedMut}mut builder: #{Builder}) -> Result<#{Builder}, #{Error}>", + "pub(crate) fn $fnName(value: &[u8], ${unusedMut}mut builder: #{Builder}) -> Result<#{Builder}, #{Error}>", "Builder" to structureShape.builderSymbol(symbolProvider), *codegenScope ) { @@ -122,7 +127,7 @@ class JsonParserGenerator( val fnName = symbolProvider.deserializeFunctionName(shape) + "_payload" return RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( - "pub fn $fnName(input: &[u8]) -> Result<#{Shape}, #{Error}>", + "pub(crate) fn $fnName(input: &[u8]) -> Result<#{Shape}, #{Error}>", *codegenScope, "Shape" to symbolProvider.toSymbol(shape) ) { @@ -170,7 +175,7 @@ class JsonParserGenerator( private fun orEmptyJson(): RuntimeType = RuntimeType.forInlineFun("or_empty_doc", jsonDeserModule) { it.rust( """ - pub fn or_empty_doc(data: &[u8]) -> &[u8] { + pub(crate) fn or_empty_doc(data: &[u8]) -> &[u8] { if data.is_empty() { b"{}" } else { @@ -278,16 +283,22 @@ class JsonParserGenerator( private fun RustWriter.deserializeCollection(shape: CollectionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() + val returnUnconstrainedType = shape.canReachConstrainedShape(model, symbolProvider) + val returnType = if (returnUnconstrainedType) { + unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + symbolProvider.toSymbol(shape) + } val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have lists with names prefixed with `__listOf__`, // which become `__list_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. it.rustBlockTemplate( """ ##[allow(clippy::type_complexity, non_snake_case)] - pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "Shape" to symbolProvider.toSymbol(shape), + "ReturnType" to returnType, *codegenScope, ) { startArrayOrNull { @@ -313,7 +324,11 @@ class JsonParserGenerator( } } } - rust("Ok(Some(items))") + if (returnUnconstrainedType) { + rust("Ok(Some(#{T}(items)))", returnType) + } else { + rust("Ok(Some(items))") + } } } } @@ -330,7 +345,7 @@ class JsonParserGenerator( it.rustBlockTemplate( """ ##[allow(clippy::type_complexity, non_snake_case)] - pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, "Shape" to symbolProvider.toSymbol(shape), @@ -363,16 +378,16 @@ class JsonParserGenerator( private fun RustWriter.deserializeStruct(shape: StructureShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) - val returnBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) + val returnBuilder = shape.canReachConstrainedShape(model, symbolProvider) val returnType = if (returnBuilder) { - shape.builderSymbol(symbolProvider) + unconstrainedShapeSymbolProvider.toSymbol(shape) } else { symbol } val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ - pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, "ReturnType" to returnType, @@ -412,7 +427,7 @@ class JsonParserGenerator( val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ - pub fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, *codegenScope, diff --git a/rust-runtime/inlineable/src/validation.rs b/rust-runtime/inlineable/src/validation.rs index 49706de93e..a3eed7b97c 100644 --- a/rust-runtime/inlineable/src/validation.rs +++ b/rust-runtime/inlineable/src/validation.rs @@ -1,10 +1,10 @@ -pub trait Validate { +pub(crate) trait Validate { type Unvalidated; } #[derive(Debug, Clone)] #[allow(dead_code)] -pub enum Validated { +pub(crate) enum Validated { Validated(T), Unvalidated(T::Unvalidated), } From 324dcf6681eb8fdf6bdb427358e87b2e98cdc3c7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Sun, 10 Apr 2022 21:20:02 +0200 Subject: [PATCH 013/255] Unit tests for UnconstrainedListGenerator --- .../server/smithy/ServerCodegenVisitor.kt | 4 +- .../generators/ServerBuilderGenerator.kt | 2 +- ...rator.kt => UnconstrainedListGenerator.kt} | 9 +- .../smithy/testutil/ServerTestHelpers.kt | 41 +++++ .../ServerCombinedErrorGeneratorTest.kt | 3 +- .../UnconstrainedListGeneratorTest.kt | 148 ++++++++++++++++++ .../rust/codegen/testutil/TestHelpers.kt | 6 +- 7 files changed, 202 insertions(+), 11 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{ConstrainedListGenerator.kt => UnconstrainedListGenerator.kt} (94%) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index e5b14ea862..133060a22c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -190,7 +190,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") rustCrate.withModule(RustModule.private("validation")) { writer -> - ConstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() + UnconstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 92d851caa7..d04114100c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -63,7 +63,7 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { - Attribute.Derives(setOf(RuntimeType.Debug)).render(writer) + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. Attribute.NonExhaustive.render(writer) writer.rustBlock("pub enum ValidationFailure") { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt similarity index 94% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index 252d187478..c98d1c6264 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -17,8 +17,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.wrapValidated -// TODO Rename to Unconstrained? -class ConstrainedListGenerator( +class UnconstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, @@ -31,7 +30,7 @@ class ConstrainedListGenerator( // TODO Unit test that this is pub(crate). - // TODO Some of these can be come private properties. + // TODO Some of these can become private properties. val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() val name = symbol.name @@ -64,8 +63,8 @@ class ConstrainedListGenerator( } } - ##[derive(Debug)] - pub struct $constraintViolationName(#{InnerConstraintViolationSymbol}); + ##[derive(Debug, PartialEq)] + pub struct $constraintViolationName(pub(crate) #{InnerConstraintViolationSymbol}); impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { type Error = $constraintViolationName; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 518282e063..b98b31ae76 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -7,11 +7,24 @@ package software.amazon.smithy.rust.codegen.server.smithy.testutil import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.smithy.CodegenConfig +import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenMode +import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget +import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.testutil.testRustSettings +import software.amazon.smithy.rust.codegen.testutil.testSymbolProvider val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( runtimeConfig = TestRuntimeConfig, @@ -27,9 +40,37 @@ val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( handleRequired = true ) +fun serverTestCodegenContext( + model: Model, + serviceShape: ServiceShape? = null, + settings: RustSettings = testRustSettings(model), + mode: CodegenMode = CodegenMode.Client +): CodegenContext = CodegenContext( + model, + serverTestSymbolProvider(model), + TestRuntimeConfig, + // TODO We should not fabricate a service shape out of thin air here, but rather look it up in the model. + serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), + ShapeId.from("test#Protocol"), + settings, + mode +) + fun serverTestSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSymbolProvider = RustCodegenServerPlugin.baseSymbolProvider( model, serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ServerTestSymbolVisitorConfig ) + +/** + * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. + */ +fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { + StructureGenerator(model, symbolProvider, writer, this).render(CodegenTarget.SERVER) + val modelBuilder = ServerBuilderGenerator(serverTestCodegenContext(model), this) + modelBuilder.render(writer) + writer.implBlock(this, symbolProvider) { + modelBuilder.renderConvenienceMethod(this) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt index 6dc193b063..66482fba8b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer @@ -50,7 +51,7 @@ class ServerCombinedErrorGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) project.withModule(RustModule.public("error")) { writer -> listOf("FooException", "ComplexError", "InvalidGreeting").forEach { - model.lookup("error#$it").renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER) + model.lookup("error#$it").serverRenderWithModelBuilder(model, symbolProvider, writer) } val generator = ServerCombinedErrorGenerator(model, symbolProvider, model.lookup("error#Greeting")) generator.render(writer) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt new file mode 100644 index 0000000000..dca47a4f86 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -0,0 +1,148 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.renderWithModelBuilder +import software.amazon.smithy.rust.codegen.testutil.unitTest +import software.amazon.smithy.rust.codegen.util.lookup + +class UnconstrainedListGeneratorTest { + private val baseModelString = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + list: ListA + } + """ + + @Test + fun `it should generate unconstrained lists`() { + val model = + """ + $baseModelString + + list ListA { + member: ListB + } + + list ListB { + member: StructureC + } + + structure StructureC { + @required + int: Integer, + + @required + string: String + } + """.asSmithyModel() + val symbolProvider = serverTestSymbolProvider(model) + + val serviceShape = model.lookup("test#TestService") + val listA = model.lookup("test#ListA") + val listB = model.lookup("test#ListB") + + val project = TestWorkspace.testProject(symbolProvider) + + project.withModule(RustModule.public("model")) { writer -> + model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) + } + + project.withModule(RustModule.public("validation")) { writer -> + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(listA, listB).forEach { + UnconstrainedListGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + writer, + it + ).render() + } + + writer.unitTest( + name = "list_a_unconstrained_fail_to_constrain_with_first_error", + test = """ + let c_builder1 = crate::model::StructureC::builder().int(69); + let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected_err = + list_a_unconstrained::ValidationFailure(list_b_unconstrained::ValidationFailure( + crate::model::structure_c::ValidationFailure::MissingString, + )); + + use std::convert::TryFrom; + assert_eq!( + expected_err, + Vec::>::try_from(list_a_unconstrained).unwrap_err() + ); + """ + ) + + writer.unitTest( + name = "list_a_unconstrained_succeed_to_constrain", + test = """ + let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected = vec![vec![crate::model::StructureC { + string: String::from("david"), + int: 69 + }]]; + + use std::convert::TryFrom; + assert_eq!( + expected, + Vec::>::try_from(list_a_unconstrained).unwrap() + ); + """ + ) + + writer.unitTest( + name = "list_a_unconstrained_converts_into_validated", + test = """ + let c_builder = crate::model::StructureC::builder(); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let _list_a: Validated>> = list_a_unconstrained.into(); + """ + ) + + project.compileAndTest() + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt index da7f5d2284..3250f3e4eb 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt @@ -80,9 +80,11 @@ fun testCodegenContext( model, testSymbolProvider(model), TestRuntimeConfig, + // TODO We should not fabricate a service shape out of thin air here, but rather look it up in the model. serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), - settings, mode + settings, + mode ) private const val SmithyVersion = "1.0" @@ -93,7 +95,7 @@ fun String.asSmithyModel(sourceLocation: String? = null): Model { } /** - * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder + * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ fun StructureShape.renderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter, forWhom: CodegenTarget = CodegenTarget.CLIENT) { StructureGenerator(model, symbolProvider, writer, this).render(forWhom) From 167a5dae611d3c013cf3cb5541f5e8b48185c874 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 11 Apr 2022 12:16:17 +0200 Subject: [PATCH 014/255] Maps work SST and SRT --- codegen-server-test/model/simple.smithy | 15 +- .../ConstraintViolationSymbolProvider.kt | 64 +++++--- .../server/smithy/ServerCodegenVisitor.kt | 14 ++ .../generators/UnconstrainedListGenerator.kt | 3 +- .../generators/UnconstrainedMapGenerator.kt | 139 ++++++++++++++++++ .../smithy/rust/codegen/smithy/Constraints.kt | 4 +- .../rust/codegen/smithy/SymbolVisitor.kt | 2 + .../UnconstrainedShapeSymbolProvider.kt | 58 +++++--- .../protocols/parse/JsonParserGenerator.kt | 16 +- 9 files changed, 264 insertions(+), 51 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 004674a935..fb1231162d 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -35,7 +35,9 @@ structure ConA { optConB: ConB, - conBList: ConBList + conBList: ConBList, + + conBMap: ConBMap } structure ConB { @@ -63,7 +65,6 @@ structure ConB { // } // - list ConBList { member: AnotherList } @@ -71,3 +72,13 @@ list ConBList { list AnotherList { member: ConB } + +map ConBMap { + key: String, + value: AnotherMap +} + +map AnotherMap { + key: String, + value: ConBList +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index c5dbe00dd4..3cc570de27 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -7,8 +7,13 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -27,39 +32,52 @@ class ConstraintViolationSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { + // TODO Rename. + private val constraintViolationName = "ValidationFailure" + + private fun unconstrainedSymbolForUnconstrainedSetListMapShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape.isMapShape) + + // TODO This name is common in `UnconstrainedShapeSymbolProvider`, extract somewhere. + val unconstrainedTypeName = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" + val rustType = RustType.Opaque(constraintViolationName, namespace) + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Validation.filename) + .build() + } + override fun toSymbol(shape: Shape): Symbol = - when { - shape.isListShape -> { - val listShape = shape.asListShape().get() - check(listShape.canReachConstrainedShape(model, base)) + when (shape) { + is ListShape -> { + check(shape.canReachConstrainedShape(model, base)) - if (listShape.isConstrained(base)) { + if (shape.isConstrained(base)) { TODO("Constraint traits on list shapes are currently not implemented") } else { - val name = "ValidationFailure" - // TODO This name is common in `UnconstrainedShapeSymbolProvider`, extract somewhere. - val unconstrainedListName = "${listShape.id.getName(serviceShape).toPascalCase()}Unconstrained" - val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedListName.toSnakeCase())}" - val rustType = RustType.Opaque(name, namespace) - Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .namespace(rustType.namespace, "::") - .definitionFile(Validation.filename) - .build() + unconstrainedSymbolForUnconstrainedSetListMapShape(shape) } } - shape.isSetShape -> { + // TODO I can just check is CollectionShape and merge with previous arm + is SetShape -> { TODO() } - shape.isMapShape -> { - TODO() + is MapShape -> { + check(shape.canReachConstrainedShape(model, base)) + + if (shape.isConstrained(base)) { + TODO("Constraint traits on map shapes are currently not implemented") + } else { + unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + } } - shape.isStructureShape -> { - val structureShape = shape.asStructureShape().get() - check(structureShape.canReachConstrainedShape(model, base)) + is StructureShape -> { + check(shape.canReachConstrainedShape(model, base)) - val builderSymbol = structureShape.builderSymbol(base) + val builderSymbol = shape.builderSymbol(base) val name = "ValidationFailure" val namespace = builderSymbol.namespace diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 133060a22c..861a163a6b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeVisitor @@ -21,6 +22,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext @@ -195,6 +197,18 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } + override fun mapShape(shape: MapShape) { + // TODO We only have to generate this for maps that are reachable from operation + // input AND from which you can reach a shape that is constrained. + if (shape.canReachConstrainedShape(model, symbolProvider)) { + logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") + + rustCrate.withModule(RustModule.private("validation")) { writer -> + UnconstrainedMapGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() + } + } + } + /** * String Shape Visitor * diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index c98d1c6264..b72378f361 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -17,6 +17,8 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.wrapValidated +// TODO Docs +// TODO Can we reuse this generator for sets? class UnconstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, @@ -42,7 +44,6 @@ class UnconstrainedListGenerator( val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. - // TODO I don't understand why pub(crate) suffices, when we're leaking it via the server builder validation failure enum variant definition. // TODO The implementation of the Validate trait is probably not for the correct type. There might be more than // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt new file mode 100644 index 0000000000..28aa22d0b8 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -0,0 +1,139 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape +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.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.wrapValidated + +// TODO Docs +class UnconstrainedMapGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val writer: RustWriter, + val shape: MapShape +) { + fun render() { + check(shape.canReachConstrainedShape(model, symbolProvider)) + + // TODO Save in booleans whether keys and/or values need validation. + + // TODO Unit test that this is pub(crate). + + // TODO Some of these can become private properties. + val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name + val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + val valueShape = model.expectShape(shape.value.target) + val keySymbol = if (isKeyConstrained(keyShape)) { + unconstrainedShapeSymbolProvider.toSymbol(keyShape) + } else { + symbolProvider.toSymbol(keyShape) + } + val valueSymbol = if (isValueConstrained(valueShape)) { + unconstrainedShapeSymbolProvider.toSymbol(valueShape) + } else { + symbolProvider.toSymbol(valueShape) + } + // TODO: We will need a `ConstrainedSymbolProvider` when we have constraint traits. + val constrainedSymbol = symbolProvider.toSymbol(shape) + val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name + val keyConstraintViolationSymbol = if (isKeyConstrained(keyShape)) { + constraintViolationSymbolProvider.toSymbol(keyShape) + } else { + null + } + val valueConstraintViolationSymbol = if (isValueConstrained(valueShape)) { + constraintViolationSymbolProvider.toSymbol(valueShape) + } else { + null + } + val constraintViolationCodegenScope = listOfNotNull( + keyConstraintViolationSymbol?.let { "KeyConstraintViolationSymbol" to it }, + valueConstraintViolationSymbol?.let { "ValueConstraintViolationSymbol" to it }, + ).toTypedArray() + + // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. + // TODO The implementation of the Validate trait is probably not for the correct type. There might be more than + // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint + // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. + // So we might have to newtype things. + writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + + impl #{ValidateTrait} for #{ConstrainedSymbol} { + type Unvalidated = $name; + } + + impl From<$name> for #{Validated} { + fn from(value: $name) -> Self { + Self::Unvalidated(value) + } + } + + ##[derive(Debug, PartialEq)] + pub enum $constraintViolationName { + ${ if (isKeyConstrained(keyShape)) "Key(#{KeyConstraintViolationSymbol})," else "" } + ${ if (isValueConstrained(valueShape)) "Value(#{ValueConstraintViolationSymbol})," else "" } + } + + impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { + type Error = $constraintViolationName; + + fn try_from(value: $name) -> Result { + value + .0 + .into_iter() + .map(|(k, v)| { + use std::convert::TryInto; + ${ if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(|err| Self::Error::Key(err))?;" else "" } + ${ if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(|err| Self::Error::Value(err))?;" else "" } + Ok((k, v)) + }) + .collect() + } + } + """, + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + *constraintViolationCodegenScope, + "ConstrainedSymbol" to constrainedSymbol, + "Validated" to constrainedSymbol.wrapValidated(), + "ValidateTrait" to RuntimeType.ValidateTrait(), + ) + } + } + + private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() + + private fun isValueConstrained(shape: Shape): Boolean = when (shape) { + is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) + is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) + is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + // TODO Constraint traits on simple shapes. + else -> false + } + +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 16aec21453..f39207db2b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Find a better place for these primitives. Probably rust.codegen.util +// TODO Unit test these functions. // TODO This will work fine if we include RequiredTrait too won't it? fun Shape.hasConstraintTrait() = @@ -64,7 +65,7 @@ fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvid } val key = model.expectShape(this.key.target) - val value = model.expectShape(this.key.target) + val value = model.expectShape(this.value.target) return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider) } @@ -81,6 +82,7 @@ private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Mode is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + // TODO Constraint traits on simple shapes. else -> false } } \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 8ba7524913..6f39d6a569 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -110,10 +110,12 @@ fun Symbol.makeOptional(): Symbol = .build() } +// TODO This can be written in terms of `mapRustType`. fun Symbol.wrapValidated(): Symbol { val rustType = RustType.Validated(this.rustType()) return Symbol.builder() .rustType(rustType) + // TODO This is a bug. Grep for all `addReference(this)`, they are probably bugs. .addReference(this) .name(rustType.name) .build() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index d96aab9ce3..0775f26b10 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -7,8 +7,13 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol @@ -20,37 +25,48 @@ class UnconstrainedShapeSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { + private fun unconstrainedSymbolForUnconstrainedSetListMapShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape.isMapShape) + + val name = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" + val rustType = RustType.Opaque(name, namespace) + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Validation.filename) + .build() + } + override fun toSymbol(shape: Shape): Symbol = - when { - shape.isListShape -> { - val listShape = shape.asListShape().get() - check(listShape.canReachConstrainedShape(model, base)) + when (shape) { + is ListShape -> { + check(shape.canReachConstrainedShape(model, base)) - if (listShape.isConstrained(base)) { + if (shape.isConstrained(base)) { TODO("Constraint traits on list shapes are currently not implemented") } else { - val name = "${listShape.id.getName(serviceShape).toPascalCase()}Unconstrained" - val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" - val rustType = RustType.Opaque(name, namespace) - Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .namespace(rustType.namespace, "::") - .definitionFile(Validation.filename) - .build() + unconstrainedSymbolForUnconstrainedSetListMapShape(shape) } } - shape.isSetShape -> { + // TODO I can just check is CollectionShape and merge with previous arm + is SetShape -> { TODO() } - shape.isMapShape -> { - TODO() + is MapShape -> { + check(shape.canReachConstrainedShape(model, base)) + + if (shape.isConstrained(base)) { + TODO("Constraint traits on map shapes are currently not implemented") + } else { + unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + } } - shape.isStructureShape -> { - val structureShape = shape.asStructureShape().get() - check(structureShape.canReachConstrainedShape(model, base)) + is StructureShape -> { + check(shape.canReachConstrainedShape(model, base)) - structureShape.builderSymbol(base) + shape.builderSymbol(base) } // TODO Simple shapes can have constraint traits. else -> base.toSymbol(shape) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 5a16d4295e..270f187ae2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -339,16 +339,22 @@ class JsonParserGenerator( val keyTarget = model.expectShape(shape.key.target) as StringShape val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() + val returnUnconstrainedType = shape.canReachConstrainedShape(model, symbolProvider) + val returnType = if (returnUnconstrainedType) { + unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + symbolProvider.toSymbol(shape) + } val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have maps with names prefixed with `__mapOf__`, // which become `__map_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. it.rustBlockTemplate( """ ##[allow(clippy::type_complexity, non_snake_case)] - pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> + pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "Shape" to symbolProvider.toSymbol(shape), + "ReturnType" to returnType, *codegenScope, ) { startObjectOrNull { @@ -368,7 +374,11 @@ class JsonParserGenerator( } } } - rust("Ok(Some(map))") + if (returnUnconstrainedType) { + rust("Ok(Some(#{T}(map)))", returnType) + } else { + rust("Ok(Some(map))") + } } } } From a9a33d50d03c3254016b0e84e030c9a2da8e75b1 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 11 Apr 2022 13:04:49 +0200 Subject: [PATCH 015/255] Unit tests for UnconstrainedMapGenerator --- .../generators/UnconstrainedMapGenerator.kt | 26 +-- .../UnconstrainedListGeneratorTest.kt | 37 ++-- .../UnconstrainedMapGeneratorTest.kt | 172 ++++++++++++++++++ 3 files changed, 197 insertions(+), 38 deletions(-) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 28aa22d0b8..45770bbcf1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -33,9 +33,6 @@ class UnconstrainedMapGenerator( ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - - // TODO Save in booleans whether keys and/or values need validation. - // TODO Unit test that this is pub(crate). // TODO Some of these can become private properties. @@ -57,19 +54,17 @@ class UnconstrainedMapGenerator( // TODO: We will need a `ConstrainedSymbolProvider` when we have constraint traits. val constrainedSymbol = symbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name - val keyConstraintViolationSymbol = if (isKeyConstrained(keyShape)) { - constraintViolationSymbolProvider.toSymbol(keyShape) - } else { - null - } - val valueConstraintViolationSymbol = if (isValueConstrained(valueShape)) { - constraintViolationSymbolProvider.toSymbol(valueShape) - } else { - null - } val constraintViolationCodegenScope = listOfNotNull( - keyConstraintViolationSymbol?.let { "KeyConstraintViolationSymbol" to it }, - valueConstraintViolationSymbol?.let { "ValueConstraintViolationSymbol" to it }, + if (isKeyConstrained(keyShape)) { + "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) + } else { + null + }, + if (isValueConstrained(valueShape)) { + "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape) + } else { + null + }, ).toTypedArray() // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. @@ -135,5 +130,4 @@ class UnconstrainedMapGenerator( // TODO Constraint traits on simple shapes. else -> false } - } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt index dca47a4f86..f111081fc2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -14,39 +14,32 @@ import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymb import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.testutil.unitTest import software.amazon.smithy.rust.codegen.util.lookup class UnconstrainedListGeneratorTest { - private val baseModelString = - """ - namespace test - - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - list: ListA - } - """ - @Test fun `it should generate unconstrained lists`() { val model = """ - $baseModelString + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + list: ListA + } list ListA { member: ListB diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt new file mode 100644 index 0000000000..ad1f29eac2 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest +import software.amazon.smithy.rust.codegen.util.lookup + +class UnconstrainedMapGeneratorTest { + @Test + fun `it should generate unconstrained maps`() { + val model = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + map: MapA + } + + map MapA { + key: String, + value: MapB + } + + map MapB { + key: String, + value: StructureC + } + + structure StructureC { + @required + int: Integer, + + @required + string: String + } + """.asSmithyModel() + val symbolProvider = serverTestSymbolProvider(model) + + val serviceShape = model.lookup("test#TestService") + val mapA = model.lookup("test#MapA") + val mapB = model.lookup("test#MapB") + + val project = TestWorkspace.testProject(symbolProvider) + + project.withModule(RustModule.public("model")) { writer -> + model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) + } + + project.withModule(RustModule.public("validation")) { writer -> + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(mapA, mapB).forEach { + UnconstrainedMapGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + writer, + it + ).render() + } + + writer.unitTest( + name = "map_a_unconstrained_fail_to_constrain_with_first_error", + test = """ + let c_builder1 = crate::model::StructureC::builder().int(69); + let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyB1"), c_builder1), + (String::from("KeyB2"), c_builder2), + ]) + ); + let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyA"), map_b_unconstrained), + ]) + ); + + let expected_err = + map_a_unconstrained::ValidationFailure::Value(map_b_unconstrained::ValidationFailure::Value( + crate::model::structure_c::ValidationFailure::MissingString, + )); + + use std::convert::TryFrom; + assert_eq!( + expected_err, + std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap_err() + ); + """ + ) + + writer.unitTest( + name = "map_a_unconstrained_succeed_to_constrain", + test = """ + let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); + let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyB"), c_builder), + ]) + ); + let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyA"), map_b_unconstrained), + ]) + ); + + let expected = std::collections::HashMap::from([ + (String::from("KeyA"), std::collections::HashMap::from([ + (String::from("KeyB"), crate::model::StructureC { + int: 69, + string: String::from("david") + }), + ])) + ]); + + use std::convert::TryFrom; + assert_eq!( + expected, + std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap() + ); + """ + ) + + writer.unitTest( + name = "map_a_unconstrained_converts_into_validated", + test = """ + let c_builder = crate::model::StructureC::builder(); + let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyB"), c_builder), + ]) + ); + let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyA"), map_b_unconstrained), + ]) + ); + + let _map_a: Validated>> = map_a_unconstrained.into(); + """ + ) + + project.compileAndTest() + } + } +} From f421b19b8fefd5b87d27968bc693bd74309c6955 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 11 Apr 2022 17:38:18 +0200 Subject: [PATCH 016/255] Seems like recursive shapes now work --- codegen-server-test/model/simple.smithy | 14 ++- .../generators/ServerBuilderGenerator.kt | 97 ++++++++++++++----- .../smithy/rust/codegen/smithy/Constraints.kt | 30 +++--- .../rust/codegen/smithy/SymbolVisitor.kt | 7 +- .../smithy/generators/StructureGenerator.kt | 6 +- .../protocols/parse/JsonParserGenerator.kt | 2 + 6 files changed, 113 insertions(+), 43 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index fb1231162d..46f9d3e281 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -55,15 +55,23 @@ structure ConB { // } // // structure RecursiveShapesInputOutputNested1 { -// foo: String, -// nested: RecursiveShapesInputOutputNested2 +// @required +// recursiveMember: RecursiveShapesInputOutputNested2 // } // // structure RecursiveShapesInputOutputNested2 { -// bar: StringList, +// @required // recursiveMember: RecursiveShapesInputOutputNested1, // } + +// list ValidList { +// member: RecursiveShapesInputOutput +// } // +// structure RecursiveShapesInputOutput { +// @required +// foo: ValidList +// } list ConBList { member: AnotherList diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index d04114100c..5bfa060860 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -25,16 +25,20 @@ import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymb import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.targetNeedsValidation +import software.amazon.smithy.rust.codegen.smithy.isBoxed import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeOptional +import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.wrapValidated +import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -193,6 +197,8 @@ class ServerBuilderGenerator( val symbol = symbolProvider.toSymbol(member) val memberName = symbolProvider.toMemberName(member) + val hasBox = symbol.mapRustType { it.stripOuter() }.isBoxed() + writer.documentShape(member, model) writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") @@ -200,9 +206,18 @@ class ServerBuilderGenerator( if (member.targetNeedsValidation(model, symbolProvider)) { val validatedType = "${symbol.wrapValidated().rustType().namespace}::Validated::Validated" if (symbol.isOptional()) { - write("input.map(|v| $validatedType(v))") + if (hasBox) { + write("input.map(|v| Box::new($validatedType(*v)))") + } else { + write("input.map(|v| $validatedType(v))") + } } else { - write("$validatedType(input)") + if (hasBox) { + // TODO Add a protocol test testing this branch. + write("Box::new($validatedType(*input))") + } else { + write("$validatedType(input)") + } } } else { write("input") @@ -280,9 +295,17 @@ class ServerBuilderGenerator( ValidationFailureKind.MISSING_MEMBER -> writer.rust("${validationFailure.name()},") ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> { val targetShape = model.expectShape(validationFailure.forMember.target) + + // TODO I guess the RustBoxTrait logic could be handled by the symbol provider. + val constraintViolationSymbol = + constraintViolationSymbolProvider.toSymbol(targetShape) + .letIf(validationFailure.forMember.hasTrait()) { + it.makeRustBoxed() + } + // Note we cannot express the inner validation failure as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. - writer.rust("${validationFailure.name()}(#T),", constraintViolationSymbolProvider.toSymbol(targetShape)) + writer.rust("${validationFailure.name()}(#T),", constraintViolationSymbol) } } } @@ -359,14 +382,22 @@ class ServerBuilderGenerator( * Returns the symbol for a builder's member. * All builder members are optional, but only some are `Option`s where `T` needs to be validated. */ - private fun builderMemberSymbol(member: MemberShape): Symbol = - symbolProvider.toSymbol(member) + private fun builderMemberSymbol(member: MemberShape): Symbol { + val strippedOption = symbolProvider.toSymbol(member) // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } - // Wrap the symbol with the Cow-like `validation::Validated` type in case the target member shape needs validation. + + val hadBox = strippedOption.isBoxed() + return strippedOption + // Strip the `Box` in case the member can reach itself recursively. + .mapRustType { it.stripOuter() } + // Wrap it in the Cow-like `validation::Validated` type in case the target member shape needs validation. .letIf(member.targetNeedsValidation(model, symbolProvider)) { it.wrapValidated() } - // Ensure we end up with an `Option`. + // Box it in case the member can reach itself recursively. + .letIf(hadBox) { it.makeRustBoxed() } + // Ensure we always end up with an `Option`. .makeOptional() + } /** * Writes the code to instantiate the struct the builder builds. @@ -388,24 +419,44 @@ class ServerBuilderGenerator( withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). - builderValidationFailureForMember(member)?.let { + builderValidationFailureForMember(member)?.also { validationFailure -> // TODO Remove `TryInto` import when we switch to 2021 edition. - rustTemplate( - """ - .map(|v| match v { - #{Validated}::Validated(x) => Ok(x), - #{Validated}::Unvalidated(x) => { - use std::convert::TryInto; - x.try_into() - } - }) - .map(|v| v.map_err(|err| ValidationFailure::${it.name()}(err))) - .transpose()? - """, - "Validated" to RuntimeType.Validated() - ) + val hasBox = builderMemberSymbol(member) + .mapRustType { it.stripOuter() } + .isBoxed() + if (hasBox) { + rustTemplate( + """ + .map(|v| match *v { + #{Validated}::Validated(x) => Ok(Box::new(x)), + #{Validated}::Unvalidated(x) => { + use std::convert::TryInto; + Ok(Box::new(x.try_into()?)) + } + }) + .map(|v| v.map_err(|err| ValidationFailure::${validationFailure.name()}(Box::new(err)))) + .transpose()? + """, + "Validated" to RuntimeType.Validated() + ) + } else { + rustTemplate( + """ + .map(|v| match v { + #{Validated}::Validated(x) => Ok(x), + #{Validated}::Unvalidated(x) => { + use std::convert::TryInto; + x.try_into() + } + }) + .map(|v| v.map_err(|err| ValidationFailure::${validationFailure.name()}(err))) + .transpose()? + """, + "Validated" to RuntimeType.Validated() + ) + } } - builderMissingFieldForMember(member)?.let { + builderMissingFieldForMember(member)?.also { rust(".ok_or(ValidationFailure::${it.name()})?") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index f39207db2b..2ff68a432a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -36,30 +36,30 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { } } -fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { +fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { if (this.isConstrained(symbolProvider)) { return true } return this.members().map { model.expectShape(it.target) }.any { - it.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(it, model, symbolProvider) + it.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(it, model, symbolProvider, visited) } } -fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { +fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { if (this.isConstrained(symbolProvider)) { return true } val member = model.expectShape(this.member.target) - return member.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(member, model, symbolProvider) + return member.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(member, model, symbolProvider, visited) } fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) -fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean { +fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { if (this.isConstrained(symbolProvider)) { return true } @@ -67,21 +67,27 @@ fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvid val key = model.expectShape(this.key.target) val value = model.expectShape(this.value.target) - return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider) + return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider, visited) } -private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model, symbolProvider: SymbolProvider): Boolean { +private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model, symbolProvider: SymbolProvider, visited: Set): Boolean { check(!shape.isConstrained(symbolProvider)) { "This function can only be called with unconstrained shapes" } - // TODO - if (shape.id.name == "RecursiveShapesInputOutputNested1") { + if (visited.contains(shape)) { return false } +// // TODO +// if (shape.id.name == "RecursiveShapesInputOutputNested1") { +// return false +// } + + val newVisited = setOf(shape).plus(visited) + return when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) - is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) + is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) + is MapShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) // TODO Constraint traits on simple shapes. else -> false } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 6f39d6a569..8c7b147c94 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -234,9 +234,12 @@ class SymbolVisitor( } /** - * Produce `Box` when the shape has the `RustBoxTrait` + * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with + * [RustBoxTrait]; otherwise returns [symbol] unchanged. + * + * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. */ - private fun handleRustBoxing(symbol: Symbol, shape: Shape): Symbol { + private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol { return if (shape.hasTrait()) { val rustType = RustType.Box(symbol.rustType()) with(Symbol.builder()) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index b2a64e4de3..60abafb59c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -76,9 +76,9 @@ fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvid // closure. // Perhaps I can simply do `PathFinder.search(shape, "")` with an empty selector. val symbol = symbolProvider.toSymbol(targetShape) - if (symbol.name == "RecursiveShapesInputOutputNested1") { - return false - } +// if (symbol.name == "RecursiveShapesInputOutputNested1") { +// return false +// } return when { targetShape.isListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 270f187ae2..304e0c31a4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -246,6 +246,8 @@ class JsonParserGenerator( } val symbol = symbolProvider.toSymbol(memberShape) if (symbol.isRustBoxed()) { + // Before boxing, convert into `Validated`. + rust(".map(|x| x.into())") rust(".map(Box::new)") } } From 7b3152dceb663df39aac1a56682e68c56023f03a Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 20 Apr 2022 15:19:06 +0200 Subject: [PATCH 017/255] rebase fixes --- .../generators/ServerBuilderGenerator.kt | 8 ++--- .../smithy/testutil/ServerTestHelpers.kt | 4 +-- .../rust/codegen/smithy/SymbolVisitor.kt | 30 ++++++++++++------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 5bfa060860..1e4d43897b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,8 +30,8 @@ import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.targetNeedsValidation -import software.amazon.smithy.rust.codegen.smithy.isBoxed import software.amazon.smithy.rust.codegen.smithy.isOptional +import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed @@ -197,7 +197,7 @@ class ServerBuilderGenerator( val symbol = symbolProvider.toSymbol(member) val memberName = symbolProvider.toMemberName(member) - val hasBox = symbol.mapRustType { it.stripOuter() }.isBoxed() + val hasBox = symbol.mapRustType { it.stripOuter() }.isRustBoxed() writer.documentShape(member, model) writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { @@ -387,7 +387,7 @@ class ServerBuilderGenerator( // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } - val hadBox = strippedOption.isBoxed() + val hadBox = strippedOption.isRustBoxed() return strippedOption // Strip the `Box` in case the member can reach itself recursively. .mapRustType { it.stripOuter() } @@ -423,7 +423,7 @@ class ServerBuilderGenerator( // TODO Remove `TryInto` import when we switch to 2021 edition. val hasBox = builderMemberSymbol(member) .mapRustType { it.stripOuter() } - .isBoxed() + .isRustBoxed() if (hasBox) { rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index b98b31ae76..b9626720ac 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -18,13 +18,11 @@ import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.testutil.testRustSettings -import software.amazon.smithy.rust.codegen.testutil.testSymbolProvider val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( runtimeConfig = TestRuntimeConfig, @@ -43,7 +41,7 @@ val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( fun serverTestCodegenContext( model: Model, serviceShape: ServiceShape? = null, - settings: RustSettings = testRustSettings(model), + settings: RustSettings = testRustSettings(), mode: CodegenMode = CodegenMode.Client ): CodegenContext = CodegenContext( model, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 8c7b147c94..b7d8dc7855 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -110,6 +110,23 @@ fun Symbol.makeOptional(): Symbol = .build() } +/** + * Make the Rust type of a symbol boxed (hold `Box`). + * + * This is idempotent and will have no change if the type is already boxed. + */ +fun Symbol.makeRustBoxed(): Symbol = + if (isRustBoxed()) { + this + } else { + val rustType = RustType.Box(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + // TODO This can be written in terms of `mapRustType`. fun Symbol.wrapValidated(): Symbol { val rustType = RustType.Validated(this.rustType()) @@ -239,17 +256,10 @@ class SymbolVisitor( * * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. */ - private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol { - return if (shape.hasTrait()) { - val rustType = RustType.Box(symbol.rustType()) - with(Symbol.builder()) { - rustType(rustType) - addReference(symbol) - name(rustType.name) - build() - } + private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = + if (shape.hasTrait()) { + symbol.makeRustBoxed() } else symbol - } private fun simpleShape(shape: SimpleShape): Symbol { return symbolBuilder(shape, SimpleShapes.getValue(shape::class)).setDefault(Default.RustDefault).build() From 4498374937472d62a2f691133351fef23aeddf1e Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 14:04:11 +0200 Subject: [PATCH 018/255] rename Validation to Constraint; sst, srt, and server-unit-tests except ServerCombinedErrorGeneratorTest all pass --- .../ConstraintViolationSymbolProvider.kt | 18 +-- .../server/smithy/ServerCodegenVisitor.kt | 27 +++- .../generators/ServerBuilderGenerator.kt | 140 +++++++++--------- .../generators/UnconstrainedListGenerator.kt | 22 +-- .../generators/UnconstrainedMapGenerator.kt | 20 +-- .../UnconstrainedShapeSymbolProviderTest.kt | 5 +- .../UnconstrainedListGeneratorTest.kt | 10 +- .../UnconstrainedMapGeneratorTest.kt | 65 ++++---- .../rust/codegen/rustlang/CargoDependency.kt | 4 +- .../smithy/rust/codegen/rustlang/RustTypes.kt | 7 +- .../rust/codegen/smithy/RuntimeTypes.kt | 6 +- .../rust/codegen/smithy/SymbolVisitor.kt | 10 +- .../UnconstrainedShapeSymbolProvider.kt | 4 +- .../smithy/generators/StructureGenerator.kt | 49 ++---- .../aws-smithy-http-server/src/validation.rs | 7 - rust-runtime/inlineable/src/constrained.rs | 10 ++ rust-runtime/inlineable/src/lib.rs | 4 +- rust-runtime/inlineable/src/validation.rs | 10 -- 18 files changed, 200 insertions(+), 218 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/validation.rs create mode 100644 rust-runtime/inlineable/src/constrained.rs delete mode 100644 rust-runtime/inlineable/src/validation.rs diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 3cc570de27..685cf1ecab 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -16,12 +16,12 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.Validation +import software.amazon.smithy.rust.codegen.smithy.Unconstrained import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -32,21 +32,20 @@ class ConstraintViolationSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { - // TODO Rename. - private val constraintViolationName = "ValidationFailure" + private val constraintViolationName = "ConstraintViolation" private fun unconstrainedSymbolForUnconstrainedSetListMapShape(shape: Shape): Symbol { check(shape is CollectionShape || shape.isMapShape) // TODO This name is common in `UnconstrainedShapeSymbolProvider`, extract somewhere. val unconstrainedTypeName = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" - val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" + val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) return Symbol.builder() .rustType(rustType) .name(rustType.name) .namespace(rustType.namespace, "::") - .definitionFile(Validation.filename) + .definitionFile(Unconstrained.filename) .build() } @@ -79,14 +78,13 @@ class ConstraintViolationSymbolProvider( val builderSymbol = shape.builderSymbol(base) - val name = "ValidationFailure" val namespace = builderSymbol.namespace - val rustType = RustType.Opaque(name, namespace) + val rustType = RustType.Opaque(constraintViolationName, namespace) Symbol.builder() .rustType(rustType) .name(rustType.name) .namespace(rustType.namespace, "::") - .definitionFile(Validation.filename) + .definitionFile(Unconstrained.filename) .build() } // TODO Simple shapes can have constraint traits. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 861a163a6b..955fb35b9b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -19,11 +19,10 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode @@ -33,6 +32,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator @@ -71,6 +71,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val codegenContext: CodegenContext private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator + private val unconstrainedModule = RustModule.private("unconstrained", "Unconstrained types for constrained shapes.") init { val symbolVisitorConfig = @@ -191,8 +192,15 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: if (shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") - rustCrate.withModule(RustModule.private("validation")) { writer -> - UnconstrainedListGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() + rustCrate.withModule(unconstrainedModule) { writer -> + UnconstrainedListGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + writer, + shape + ).render() } } } @@ -203,8 +211,15 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: if (shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") - rustCrate.withModule(RustModule.private("validation")) { writer -> - UnconstrainedMapGenerator(model, symbolProvider, unconstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape).render() + rustCrate.withModule(unconstrainedModule) { writer -> + UnconstrainedMapGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + writer, + shape + ).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 1e4d43897b..ba3ccc4c64 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -29,7 +29,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.generators.targetNeedsValidation +import software.amazon.smithy.rust.codegen.smithy.generators.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -37,7 +37,7 @@ import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.wrapValidated +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -70,17 +70,17 @@ class ServerBuilderGenerator( Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. Attribute.NonExhaustive.render(writer) - writer.rustBlock("pub enum ValidationFailure") { - validationFailures().forEach { renderValidationFailure(this, it) } + writer.rustBlock("pub enum ConstraintViolation") { + constraintViolations().forEach { renderConstraintViolation(this, it) } } - renderImplDisplayValidationFailure(writer) - writer.rust("impl std::error::Error for ValidationFailure { }") + renderImplDisplayConstraintViolation(writer) + writer.rust("impl std::error::Error for ConstraintViolation { }") // TODO This only needs to be generated for operation input shapes. - renderImplFromValidationFailureForRequestRejection(writer) + renderImplFromConstraintViolationForRequestRejection(writer) - renderImplFromBuilderForValidated(writer) + renderImplFromBuilderForMaybeConstrained(writer) renderTryFromBuilderImpl(writer) } else { @@ -101,7 +101,7 @@ class ServerBuilderGenerator( for (member in members) { renderBuilderMemberFn(this, member) - if (member.targetNeedsValidation(model, symbolProvider)) { + if (member.targetCanReachConstrainedShape(model, symbolProvider)) { renderBuilderMemberSetterFn(this, member) } } @@ -110,28 +110,28 @@ class ServerBuilderGenerator( } // TODO This impl does not take into account sensitive trait. - private fun renderImplDisplayValidationFailure(writer: RustWriter) { - writer.rustBlock("impl std::fmt::Display for ValidationFailure") { + private fun renderImplDisplayConstraintViolation(writer: RustWriter) { + writer.rustBlock("impl std::fmt::Display for ConstraintViolation") { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { rustBlock("match self") { - validationFailures().forEach { + constraintViolations().forEach { val arm = if (it.hasInner()) { - "ValidationFailure::${it.name()}(_)" + "ConstraintViolation::${it.name()}(_)" } else { - "ValidationFailure::${it.name()}" + "ConstraintViolation::${it.name()}" } - rust("""$arm => write!(f, "${validationFailureErrorMessage(it)}"),""") + rust("""$arm => write!(f, "${constraintViolationMessage(it)}"),""") } } } } } - private fun renderImplFromValidationFailureForRequestRejection(writer: RustWriter) { + private fun renderImplFromConstraintViolationForRequestRejection(writer: RustWriter) { writer.rustTemplate( """ - impl From for #{RequestRejection} { - fn from(value: ValidationFailure) -> Self { + impl From for #{RequestRejection} { + fn from(value: ConstraintViolation) -> Self { Self::BuildV2(value.into()) } } @@ -140,23 +140,23 @@ class ServerBuilderGenerator( ) } - private fun renderImplFromBuilderForValidated(writer: RustWriter) { + private fun renderImplFromBuilderForMaybeConstrained(writer: RustWriter) { writer.rust( """ impl From for #{T} { fn from(builder: Builder) -> Self { - Self::Unvalidated(builder) + Self::Unconstrained(builder) } } """, - structureSymbol.wrapValidated() + structureSymbol.wrapMaybeConstrained() ) } private fun renderBuildFn(implBlockWriter: RustWriter) { val fallibleBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) val returnType = when (fallibleBuilder) { - true -> "Result<${implBlockWriter.format(structureSymbol)}, ValidationFailure>" + true -> "Result<${implBlockWriter.format(structureSymbol)}, ConstraintViolation>" false -> implBlockWriter.format(structureSymbol) } // TODO Document when builder can fail. @@ -203,20 +203,20 @@ class ServerBuilderGenerator( writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - if (member.targetNeedsValidation(model, symbolProvider)) { - val validatedType = "${symbol.wrapValidated().rustType().namespace}::Validated::Validated" + if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + val constrainedType = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" if (symbol.isOptional()) { if (hasBox) { - write("input.map(|v| Box::new($validatedType(*v)))") + write("input.map(|v| Box::new($constrainedType(*v)))") } else { - write("input.map(|v| $validatedType(v))") + write("input.map(|v| $constrainedType(v))") } } else { if (hasBox) { // TODO Add a protocol test testing this branch. - write("Box::new($validatedType(*input))") + write("Box::new($constrainedType(*input))") } else { - write("$validatedType(input)") + write("$constrainedType(input)") } } } else { @@ -262,77 +262,77 @@ class ServerBuilderGenerator( } /** - * The kinds of validation failures that can occur when building the builder. + * The kinds of constraint violations that can occur when building the builder. */ - enum class ValidationFailureKind { + enum class ConstraintViolationKind { // A field is required but was not provided. MISSING_MEMBER, // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. CONSTRAINED_SHAPE_FAILURE, } - data class ValidationFailure(val forMember: MemberShape, val kind: ValidationFailureKind) { + data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { fun name() = when (kind) { - ValidationFailureKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ValidationFailure" + ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ConstraintViolation" } /** - * Whether the validation failure is a Rust tuple struct with one element. + * Whether the constraint violation is a Rust tuple struct with one element. */ - fun hasInner() = kind == ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE + fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE } - private fun renderValidationFailure(writer: RustWriter, validationFailure: ValidationFailure) { - if (validationFailure.kind == ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE) { + private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) { + if (constraintViolation.kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) { // TODO(): `#[doc(hidden)]` until we commit to making builders of builders public. Attribute.DocHidden.render(writer) } // TODO Add Rust docs. - when (validationFailure.kind) { - ValidationFailureKind.MISSING_MEMBER -> writer.rust("${validationFailure.name()},") - ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> { - val targetShape = model.expectShape(validationFailure.forMember.target) + when (constraintViolation.kind) { + ConstraintViolationKind.MISSING_MEMBER -> writer.rust("${constraintViolation.name()},") + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { + val targetShape = model.expectShape(constraintViolation.forMember.target) // TODO I guess the RustBoxTrait logic could be handled by the symbol provider. val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape) - .letIf(validationFailure.forMember.hasTrait()) { + .letIf(constraintViolation.forMember.hasTrait()) { it.makeRustBoxed() } - // Note we cannot express the inner validation failure as `>::Error`, because `T` might + // Note we cannot express the inner constraint violation as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. - writer.rust("${validationFailure.name()}(#T),", constraintViolationSymbol) + writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } } } - private fun validationFailureErrorMessage(validationFailure: ValidationFailure): String { - val memberName = symbolProvider.toMemberName(validationFailure.forMember) + private fun constraintViolationMessage(constraintViolation: ConstraintViolation): String { + val memberName = symbolProvider.toMemberName(constraintViolation.forMember) // TODO $structureSymbol here is not quite right because it's printing the full namespace: crate:: in the context of the user will surely be different. - return when (validationFailure.kind) { - ValidationFailureKind.MISSING_MEMBER -> "`$memberName` was not specified but it is required when building `$structureSymbol`" + return when (constraintViolation.kind) { + ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not specified but it is required when building `$structureSymbol`" // TODO Nest errors. - ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE -> "validation failure occurred building member `$memberName` when building `$structureSymbol`" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `$structureSymbol`" } } - private fun validationFailures() = members.flatMap { member -> + private fun constraintViolations() = members.flatMap { member -> listOfNotNull( builderMissingFieldForMember(member), - builderValidationFailureForMember(member), + builderConstraintViolationForMember(member), ) } /** - * Returns the builder failure associated with the `member` field if its target requires validation. + * Returns the builder failure associated with the `member` field if its target is constrained. */ - private fun builderValidationFailureForMember(member: MemberShape) = - if (member.targetNeedsValidation(model, symbolProvider)) { - ValidationFailure(member, ValidationFailureKind.CONSTRAINED_SHAPE_FAILURE) + private fun builderConstraintViolationForMember(member: MemberShape) = + if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + ConstraintViolation(member, ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) } else { null } @@ -346,7 +346,7 @@ class ServerBuilderGenerator( if (symbolProvider.toSymbol(member).isOptional()) { null } else { - ValidationFailure(member, ValidationFailureKind.MISSING_MEMBER) + ConstraintViolation(member, ConstraintViolationKind.MISSING_MEMBER) } private fun renderTryFromBuilderImpl(writer: RustWriter) { @@ -354,7 +354,7 @@ class ServerBuilderGenerator( writer.rustTemplate( """ impl std::convert::TryFrom for #{Structure} { - type Error = ValidationFailure; + type Error = ConstraintViolation; fn try_from(builder: Builder) -> Result { builder.build() @@ -380,7 +380,7 @@ class ServerBuilderGenerator( /** * Returns the symbol for a builder's member. - * All builder members are optional, but only some are `Option`s where `T` needs to be validated. + * All builder members are optional, but only some are `Option`s where `T` needs to be constrained. */ private fun builderMemberSymbol(member: MemberShape): Symbol { val strippedOption = symbolProvider.toSymbol(member) @@ -391,8 +391,8 @@ class ServerBuilderGenerator( return strippedOption // Strip the `Box` in case the member can reach itself recursively. .mapRustType { it.stripOuter() } - // Wrap it in the Cow-like `validation::Validated` type in case the target member shape needs validation. - .letIf(member.targetNeedsValidation(model, symbolProvider)) { it.wrapValidated() } + // Wrap it in the Cow-like `constrained::Constrained` type in case the target member shape can reach a constrained shape. + .letIf(member.targetCanReachConstrainedShape(model, symbolProvider)) { it.wrapMaybeConstrained() } // Box it in case the member can reach itself recursively. .letIf(hadBox) { it.makeRustBoxed() } // Ensure we always end up with an `Option`. @@ -419,7 +419,7 @@ class ServerBuilderGenerator( withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). - builderValidationFailureForMember(member)?.also { validationFailure -> + builderConstraintViolationForMember(member)?.also { constraintViolation -> // TODO Remove `TryInto` import when we switch to 2021 edition. val hasBox = builderMemberSymbol(member) .mapRustType { it.stripOuter() } @@ -428,36 +428,36 @@ class ServerBuilderGenerator( rustTemplate( """ .map(|v| match *v { - #{Validated}::Validated(x) => Ok(Box::new(x)), - #{Validated}::Unvalidated(x) => { + #{MaybeConstrained}::Constrained(x) => Ok(Box::new(x)), + #{MaybeConstrained}::Unconstrained(x) => { use std::convert::TryInto; Ok(Box::new(x.try_into()?)) } }) - .map(|v| v.map_err(|err| ValidationFailure::${validationFailure.name()}(Box::new(err)))) + .map(|v| v.map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err)))) .transpose()? """, - "Validated" to RuntimeType.Validated() + "MaybeConstrained" to RuntimeType.MaybeConstrained() ) } else { rustTemplate( """ .map(|v| match v { - #{Validated}::Validated(x) => Ok(x), - #{Validated}::Unvalidated(x) => { + #{MaybeConstrained}::Constrained(x) => Ok(x), + #{MaybeConstrained}::Unconstrained(x) => { use std::convert::TryInto; x.try_into() } }) - .map(|v| v.map_err(|err| ValidationFailure::${validationFailure.name()}(err))) + .map(|v| v.map_err(|err| ConstraintViolation::${constraintViolation.name()}(err))) .transpose()? """, - "Validated" to RuntimeType.Validated() + "MaybeConstrained" to RuntimeType.MaybeConstrained() ) } } builderMissingFieldForMember(member)?.also { - rust(".ok_or(ValidationFailure::${it.name()})?") + rust(".ok_or(ConstraintViolation::${it.name()})?") } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index b72378f361..5b768b4fcd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -11,11 +11,11 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.wrapValidated +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained // TODO Docs // TODO Can we reuse this generator for sets? @@ -43,8 +43,8 @@ class UnconstrainedListGenerator( val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. - // TODO The implementation of the Validate trait is probably not for the correct type. There might be more than + // TODO Strictly, `ConstrainedTrait` only needs to be implemented if this list is a struct member. + // TODO The implementation of the Constrained trait is probably not for the correct type. There might be more than // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. // So we might have to newtype things. @@ -54,13 +54,13 @@ class UnconstrainedListGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) Vec<#{InnerUnconstrainedSymbol}>); - impl #{ValidateTrait} for #{ConstrainedSymbol} { - type Unvalidated = $name; + impl #{ConstrainedTrait} for #{ConstrainedSymbol} { + type Unconstrained = $name; } - impl From<$name> for #{Validated} { + impl From<$name> for #{MaybeConstrained} { fn from(value: $name) -> Self { - Self::Unvalidated(value) + Self::Unconstrained(value) } } @@ -79,15 +79,15 @@ class UnconstrainedListGenerator( inner.try_into() }) .collect(); - res.map_err(|err| ValidationFailure(err)) + res.map_err(|err| ConstraintViolation(err)) } } """, "InnerUnconstrainedSymbol" to innerSymbol, "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, "ConstrainedSymbol" to constrainedSymbol, - "Validated" to constrainedSymbol.wrapValidated(), - "ValidateTrait" to RuntimeType.ValidateTrait(), + "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 45770bbcf1..cadef12b9d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -15,12 +15,12 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait -import software.amazon.smithy.rust.codegen.smithy.wrapValidated +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained // TODO Docs class UnconstrainedMapGenerator( @@ -67,8 +67,8 @@ class UnconstrainedMapGenerator( }, ).toTypedArray() - // TODO Strictly, `ValidateTrait` only needs to be implemented if this list is a struct member. - // TODO The implementation of the Validate trait is probably not for the correct type. There might be more than + // TODO Strictly, `ConstrainedTrait` only needs to be implemented if this list is a struct member. + // TODO The implementation of the Constrained trait is probably not for the correct type. There might be more than // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. // So we might have to newtype things. @@ -78,13 +78,13 @@ class UnconstrainedMapGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); - impl #{ValidateTrait} for #{ConstrainedSymbol} { - type Unvalidated = $name; + impl #{ConstrainedTrait} for #{ConstrainedSymbol} { + type Unconstrained = $name; } - impl From<$name> for #{Validated} { + impl From<$name> for #{MaybeConstrained} { fn from(value: $name) -> Self { - Self::Unvalidated(value) + Self::Unconstrained(value) } } @@ -115,8 +115,8 @@ class UnconstrainedMapGenerator( "ValueSymbol" to valueSymbol, *constraintViolationCodegenScope, "ConstrainedSymbol" to constrainedSymbol, - "Validated" to constrainedSymbol.wrapValidated(), - "ValidateTrait" to RuntimeType.ValidateTrait(), + "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), ) } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index ed4b1837f9..e547fa2444 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvid import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup -import java.lang.IllegalStateException class UnconstrainedShapeSymbolProviderTest { private val baseModelString = @@ -73,8 +72,8 @@ class UnconstrainedShapeSymbolProviderTest { val structureCShape = model.lookup("test#StructureC") val structureCType = symbolProvider.toSymbol(structureCShape).rustType() - listAType shouldBe RustType.Opaque("ListAUnconstrained", "crate::validation::list_a_unconstrained") - listBType shouldBe RustType.Opaque("ListBUnconstrained", "crate::validation::list_b_unconstrained") + listAType shouldBe RustType.Opaque("ListAUnconstrained", "crate::unconstrained::list_a_unconstrained") + listBType shouldBe RustType.Opaque("ListBUnconstrained", "crate::unconstrained::list_b_unconstrained") structureCType shouldBe RustType.Opaque("Builder", "crate::model::structure_c") } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt index f111081fc2..753004b36a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -69,7 +69,7 @@ class UnconstrainedListGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } - project.withModule(RustModule.public("validation")) { writer -> + project.withModule(RustModule.private("unconstrained")) { writer -> val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(listA, listB).forEach { @@ -92,8 +92,8 @@ class UnconstrainedListGeneratorTest { let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected_err = - list_a_unconstrained::ValidationFailure(list_b_unconstrained::ValidationFailure( - crate::model::structure_c::ValidationFailure::MissingString, + list_a_unconstrained::ConstraintViolation(list_b_unconstrained::ConstraintViolation( + crate::model::structure_c::ConstraintViolation::MissingString, )); use std::convert::TryFrom; @@ -125,13 +125,13 @@ class UnconstrainedListGeneratorTest { ) writer.unitTest( - name = "list_a_unconstrained_converts_into_validated", + name = "list_a_unconstrained_converts_into_constrained", test = """ let c_builder = crate::model::StructureC::builder(); let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - let _list_a: Validated>> = list_a_unconstrained.into(); + let _list_a: crate::constrained::MaybeConstrained>> = list_a_unconstrained.into(); """ ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index ad1f29eac2..45a3b93302 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -71,7 +71,7 @@ class UnconstrainedMapGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } - project.withModule(RustModule.public("validation")) { writer -> + project.withModule(RustModule.private("unconstrained")) { writer -> val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(mapA, mapB).forEach { @@ -85,35 +85,36 @@ class UnconstrainedMapGeneratorTest { ).render() } - writer.unitTest( - name = "map_a_unconstrained_fail_to_constrain_with_first_error", - test = """ - let c_builder1 = crate::model::StructureC::builder().int(69); - let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( - std::collections::HashMap::from([ - (String::from("KeyB1"), c_builder1), - (String::from("KeyB2"), c_builder2), - ]) - ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( - std::collections::HashMap::from([ - (String::from("KeyA"), map_b_unconstrained), - ]) - ); - - let expected_err = - map_a_unconstrained::ValidationFailure::Value(map_b_unconstrained::ValidationFailure::Value( - crate::model::structure_c::ValidationFailure::MissingString, - )); - - use std::convert::TryFrom; - assert_eq!( - expected_err, - std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap_err() - ); - """ - ) + // TODO This test is flaky because it depends on the order in which the `HashMap` is visited. +// writer.unitTest( +// name = "map_a_unconstrained_fail_to_constrain_with_first_error", +// test = """ +// let c_builder1 = crate::model::StructureC::builder().int(69); +// let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); +// let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( +// std::collections::HashMap::from([ +// (String::from("KeyB1"), c_builder1), +// (String::from("KeyB2"), c_builder2), +// ]) +// ); +// let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( +// std::collections::HashMap::from([ +// (String::from("KeyA"), map_b_unconstrained), +// ]) +// ); +// +// let expected_err = +// map_a_unconstrained::ConstraintViolation::Value(map_b_unconstrained::ConstraintViolation::Value( +// crate::model::structure_c::ConstraintViolation::MissingString, +// )); +// +// use std::convert::TryFrom; +// assert_eq!( +// expected_err, +// std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap_err() +// ); +// """ +// ) writer.unitTest( name = "map_a_unconstrained_succeed_to_constrain", @@ -148,7 +149,7 @@ class UnconstrainedMapGeneratorTest { ) writer.unitTest( - name = "map_a_unconstrained_converts_into_validated", + name = "map_a_unconstrained_converts_into_constrained", test = """ let c_builder = crate::model::StructureC::builder(); let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( @@ -162,7 +163,7 @@ class UnconstrainedMapGeneratorTest { ]) ); - let _map_a: Validated>> = map_a_unconstrained.into(); + let _map_a: crate::constrained::MaybeConstrained>> = map_a_unconstrained.into(); """ ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt index f16eb5da05..b6709ddc08 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt @@ -113,8 +113,8 @@ class InlineDependency( fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency = forRustFile("rest_xml_unwrapped_errors", CargoDependency.smithyXml(runtimeConfig)) - fun validation(): InlineDependency = - forRustFile("validation") + fun constrained(): InlineDependency = + forRustFile("constrained") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index ec4384963b..6cf490b87c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.rustlang -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.util.dq @@ -105,8 +104,8 @@ sealed class RustType { } } - data class Validated(override val member: RustType): RustType(), Container { - val runtimeType: RuntimeType = RuntimeType.Validated() + data class MaybeConstrained(override val member: RustType): RustType(), Container { + val runtimeType: RuntimeType = RuntimeType.MaybeConstrained() override val name = runtimeType.name!! override val namespace = runtimeType.namespace } @@ -194,7 +193,7 @@ fun RustType.render(fullyQualified: Boolean = true): String { is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" is RustType.Opaque -> this.name - is RustType.Validated -> "${this.name}<${this.member.render(fullyQualified)}>" + is RustType.MaybeConstrained -> "${this.name}<${this.member.render(fullyQualified)}>" } return "$namespace$base" } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt index cc0489ba9c..2aa6f2bad3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt @@ -20,7 +20,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.util.orNull -import java.util.Optional +import java.util.* /** * Location of the runtime crates (aws-smithy-http, aws-smithy-types etc.) @@ -226,8 +226,8 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n func, CargoDependency.SmithyProtocolTestHelpers(runtimeConfig), "aws_smithy_protocol_test" ) - fun ValidateTrait() = RuntimeType("Validate", InlineDependency.validation(), namespace = "crate::validation") - fun Validated() = RuntimeType("Validated", InlineDependency.validation(), namespace = "crate::validation") + fun ConstrainedTrait() = RuntimeType("Constrained", InlineDependency.constrained(), namespace = "crate::constrained") + fun MaybeConstrained() = RuntimeType("MaybeConstrained", InlineDependency.constrained(), namespace = "crate::constrained") val http = CargoDependency.Http.asType() fun Http(path: String): RuntimeType = diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index b7d8dc7855..6572696270 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -90,8 +90,7 @@ val Operations = SymbolLocation("operation") val Serializers = SymbolLocation("serializer") val Inputs = SymbolLocation("input") val Outputs = SymbolLocation("output") -// TODO Rename to constraints? -val Validation = SymbolLocation("validation") +val Unconstrained = SymbolLocation("unconstrained") /** * Make the Rust type of a symbol optional (hold `Option`) @@ -128,11 +127,11 @@ fun Symbol.makeRustBoxed(): Symbol = } // TODO This can be written in terms of `mapRustType`. -fun Symbol.wrapValidated(): Symbol { - val rustType = RustType.Validated(this.rustType()) +// TODO isMaybeConstrained. Make it like the others. +fun Symbol.wrapMaybeConstrained(): Symbol { + val rustType = RustType.MaybeConstrained(this.rustType()) return Symbol.builder() .rustType(rustType) - // TODO This is a bug. Grep for all `addReference(this)`, they are probably bugs. .addReference(this) .name(rustType.name) .build() @@ -143,6 +142,7 @@ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { val newType = f(this.rustType()) return Symbol.builder() .rustType(newType) + // TODO This is a bug. Grep for all `addReference(this)`, maybe they are too. .addReference(this) .name(newType.name) .build() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 0775f26b10..c1b8434ee5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -29,13 +29,13 @@ class UnconstrainedShapeSymbolProvider( check(shape is CollectionShape || shape.isMapShape) val name = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" - val namespace = "crate::${Validation.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" + val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() .rustType(rustType) .name(rustType.name) .namespace(rustType.namespace, "::") - .definitionFile(Validation.filename) + .definitionFile(Unconstrained.filename) .build() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 60abafb59c..f592b76a01 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -9,8 +9,10 @@ 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.CollectionShape +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.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait @@ -55,39 +57,14 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } // TODO Perhaps move these into `StructureGenerator`? - -fun MemberShape.targetNeedsValidation(model: Model, symbolProvider: SymbolProvider): Boolean { - val targetShape = model.expectShape(this.target) - -// val memberSymbol = symbolProvider.toSymbol(this) -// val targetSymbol = symbolProvider.toSymbol(targetShape) -// val hasRustBoxTrait = this.hasTrait() -// println("hasRustBoxTrait = $hasRustBoxTrait") -// println(symbol.name) - - // TODO We have a problem with recursion. In the case of recursive shapes whose members are all not `@required`, this - // function recurses indefinitely, but it should return the current member _does not_ require validation. - // We can detect recursion by checking the `RustBoxTrait` on the member, but we can't inspect the cycle. - // An easy way to determine whether a shape member requires validation would be if we defined the "closure of a shape `S`" - // as all the shapes reachable from `S`. Then a member shape whose target is a structure shape `S` requires - // validation if and only if any of the shapes in its closure is `@required` (or is a constrained shape, when we implement those). - // - // I see `TopologicalIndex.java` in Smithy allows me to get the _recursive_ closure of a shape, but I need its entire - // closure. - // Perhaps I can simply do `PathFinder.search(shape, "")` with an empty selector. - val symbol = symbolProvider.toSymbol(targetShape) -// if (symbol.name == "RecursiveShapesInputOutputNested1") { -// return false -// } - - return when { - targetShape.isListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) - targetShape.isSetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) - targetShape.isMapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) - targetShape.isStructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) +fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + when (val targetShape = model.expectShape(this.target)) { + is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) + is SetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) + is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) + is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) else -> false } -} /** * The name of the builder's setter the server deserializer should use. @@ -190,14 +167,14 @@ class StructureGenerator( } } - private fun renderValidateImpl() { + private fun renderConstrainedTraitImpl() { writer.rust( """ impl #T for $name { - type Unvalidated = #T; + type Unconstrained = #T; } """, - RuntimeType.ValidateTrait(), + RuntimeType.ConstrainedTrait(), shape.builderSymbol(symbolProvider) ) } @@ -249,8 +226,8 @@ class StructureGenerator( renderDebugImpl() // TODO This only needs to be called in the server, for structures that are reachable from operation input AND - // that require validation. It's probably best that we move it to an entirely different class. - renderValidateImpl() + // that are constrained. It's probably best that we move it to an entirely different class. + renderConstrainedTraitImpl() } private fun RustWriter.forEachMember( diff --git a/rust-runtime/aws-smithy-http-server/src/validation.rs b/rust-runtime/aws-smithy-http-server/src/validation.rs deleted file mode 100644 index 7f09f0b68d..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/validation.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub trait Validate { - type Unvalidated; -} -pub enum Validated { - Validated(T), - Unvalidated(T::Unvalidated), -} diff --git a/rust-runtime/inlineable/src/constrained.rs b/rust-runtime/inlineable/src/constrained.rs new file mode 100644 index 0000000000..c74ac4a456 --- /dev/null +++ b/rust-runtime/inlineable/src/constrained.rs @@ -0,0 +1,10 @@ +pub(crate) trait Constrained { + type Unconstrained; +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub(crate) enum MaybeConstrained { + Constrained(T), + Unconstrained(T::Unconstrained), +} diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index 9ff1aa8cfe..bb7686de2c 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0. */ +#[allow(unused)] +mod constrained; #[allow(dead_code)] mod ec2_query_errors; #[allow(dead_code)] @@ -15,8 +17,6 @@ mod rest_xml_unwrapped_errors; mod rest_xml_wrapped_errors; #[allow(unused)] mod server_operation_handler_trait; -#[allow(unused)] -mod validation; // This test is outside of uuid.rs to enable copying the entirety of uuid.rs into the SDK without // requiring a proptest dependency diff --git a/rust-runtime/inlineable/src/validation.rs b/rust-runtime/inlineable/src/validation.rs deleted file mode 100644 index a3eed7b97c..0000000000 --- a/rust-runtime/inlineable/src/validation.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub(crate) trait Validate { - type Unvalidated; -} - -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub(crate) enum Validated { - Validated(T), - Unvalidated(T::Unvalidated), -} From c15017a411e5e55d3045a81763fb7495694fbccb Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 14:22:01 +0200 Subject: [PATCH 019/255] all server-unit-tests pass --- .../smithy/generators/ServerCombinedErrorGeneratorTest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt index 66482fba8b..4310e0be84 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt @@ -10,12 +10,10 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.testutil.unitTest import software.amazon.smithy.rust.codegen.util.lookup @@ -59,7 +57,7 @@ class ServerCombinedErrorGeneratorTest { writer.unitTest( name = "generates_combined_error_enums", test = """ - let variant = InvalidGreeting::builder().message("an error").build(); + let variant = InvalidGreeting { message: String::from("an error") }; assert_eq!(format!("{}", variant), "InvalidGreeting: an error"); assert_eq!(variant.message(), "an error"); assert_eq!( From 03f1904b44758b5d9586078a0e3ddd04e1d5fb46 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 14:36:41 +0200 Subject: [PATCH 020/255] small TODOs gone --- .../ConstraintViolationSymbolProvider.kt | 25 +++++++---------- .../UnconstrainedShapeSymbolProvider.kt | 27 +++++++++---------- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 685cf1ecab..7b1b67e096 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -8,10 +8,8 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords @@ -23,7 +21,7 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.util.toPascalCase +import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForCollectionOrMapShape import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Unit tests. @@ -34,11 +32,10 @@ class ConstraintViolationSymbolProvider( ) : WrappingSymbolProvider(base) { private val constraintViolationName = "ConstraintViolation" - private fun unconstrainedSymbolForUnconstrainedSetListMapShape(shape: Shape): Symbol { + private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { check(shape is CollectionShape || shape.isMapShape) - // TODO This name is common in `UnconstrainedShapeSymbolProvider`, extract somewhere. - val unconstrainedTypeName = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val unconstrainedTypeName = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) return Symbol.builder() @@ -51,26 +48,22 @@ class ConstraintViolationSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is ListShape -> { + is CollectionShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("Constraint traits on list shapes are currently not implemented") + TODO("The `length` constraint trait on collection shapes is currently not implemented") } else { - unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } } - // TODO I can just check is CollectionShape and merge with previous arm - is SetShape -> { - TODO() - } is MapShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("Constraint traits on map shapes are currently not implemented") + TODO("The `length` constraint trait on map shapes is currently not implemented") } else { - unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } } is StructureShape -> { @@ -87,7 +80,7 @@ class ConstraintViolationSymbolProvider( .definitionFile(Unconstrained.filename) .build() } - // TODO Simple shapes can have constraint traits. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. else -> base.toSymbol(shape) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index c1b8434ee5..14e0b7b070 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -8,10 +8,8 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords @@ -20,15 +18,20 @@ import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase +fun unconstrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { + check(shape is CollectionShape || shape.isMapShape) + return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" +} + class UnconstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { - private fun unconstrainedSymbolForUnconstrainedSetListMapShape(shape: Shape): Symbol { + private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { check(shape is CollectionShape || shape.isMapShape) - val name = "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" + val name = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() @@ -41,26 +44,22 @@ class UnconstrainedShapeSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is ListShape -> { + is CollectionShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("Constraint traits on list shapes are currently not implemented") + TODO("The `length` constraint trait on collection shapes is currently not implemented") } else { - unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } } - // TODO I can just check is CollectionShape and merge with previous arm - is SetShape -> { - TODO() - } is MapShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("Constraint traits on map shapes are currently not implemented") + TODO("The `length` constraint trait on map shapes is currently not implemented") } else { - unconstrainedSymbolForUnconstrainedSetListMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } } is StructureShape -> { @@ -68,7 +67,7 @@ class UnconstrainedShapeSymbolProvider( shape.builderSymbol(base) } - // TODO Simple shapes can have constraint traits. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. else -> base.toSymbol(shape) } } From 5ec83d10682c76f9b6d4634d963fe6ff15d15f12 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 15:28:19 +0200 Subject: [PATCH 021/255] sets can't reach constrained shapes --- codegen-server-test/model/simple.smithy | 18 +++++++++++---- .../ConstraintViolationSymbolProvider.kt | 22 +++++++++++-------- .../UnconstrainedShapeSymbolProvider.kt | 15 ++++++++----- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 46f9d3e281..fd41e81b71 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -31,13 +31,15 @@ structure AnOperationOutput { structure ConA { @required - conB: ConB, + // conB: ConB, - optConB: ConB, + // optConB: ConB, - conBList: ConBList, + // conBList: ConBList, - conBMap: ConBMap + conBSet: ConBSet, + + // conBMap: ConBMap } structure ConB { @@ -81,6 +83,14 @@ list AnotherList { member: ConB } +set ConBSet { + member: AnotherSet +} + +set AnotherSet { + member: String +} + map ConBMap { key: String, value: AnotherMap diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 7b1b67e096..10e1ba6f10 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -7,9 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords @@ -21,7 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForCollectionOrMapShape +import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForListOrMapShape import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Unit tests. @@ -32,10 +33,10 @@ class ConstraintViolationSymbolProvider( ) : WrappingSymbolProvider(base) { private val constraintViolationName = "ConstraintViolation" - private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is CollectionShape || shape.isMapShape) + private fun unconstrainedSymbolForListOrMapShape(shape: Shape): Symbol { + check(shape is ListShape || shape is MapShape) - val unconstrainedTypeName = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) + val unconstrainedTypeName = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) return Symbol.builder() @@ -48,13 +49,16 @@ class ConstraintViolationSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is CollectionShape -> { + is SetShape -> { + TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") + } + is ListShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on collection shapes is currently not implemented") + TODO("The `length` constraint trait on list shapes is currently not implemented") } else { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } } is MapShape -> { @@ -63,7 +67,7 @@ class ConstraintViolationSymbolProvider( if (shape.isConstrained(base)) { TODO("The `length` constraint trait on map shapes is currently not implemented") } else { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } } is StructureShape -> { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 14e0b7b070..ffcffbef8a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -8,8 +8,10 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords @@ -18,7 +20,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -fun unconstrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { +fun unconstrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceShape): String { check(shape is CollectionShape || shape.isMapShape) return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" } @@ -29,9 +31,9 @@ class UnconstrainedShapeSymbolProvider( private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is CollectionShape || shape.isMapShape) + check(shape is ListShape || shape is MapShape) - val name = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) + val name = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() @@ -44,11 +46,14 @@ class UnconstrainedShapeSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is CollectionShape -> { + is SetShape -> { + TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") + } + is ListShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on collection shapes is currently not implemented") + TODO("The `length` constraint trait on list shapes is currently not implemented") } else { unconstrainedSymbolForCollectionOrMapShape(shape) } From 3dfd1931bb577604e373a11aa7315cf23a9520b2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 15:49:08 +0200 Subject: [PATCH 022/255] sets of sets are not allowed, we get WARNINGS, this model tests that --- codegen-server-test/model/simple.smithy | 83 +++++++------------------ 1 file changed, 24 insertions(+), 59 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index fd41e81b71..50f15fd0a3 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -14,89 +14,54 @@ service SimpleService { @http(uri: "/operation", method: "GET") operation AnOperation { - // input: RecursiveShapesInputOutput, - // output: RecursiveShapesInputOutput, input: AnOperationInput, output: AnOperationOutput, } structure AnOperationInput { @required - conA: ConA -} + setA: SetA, -structure AnOperationOutput { - conA: ConA -} + setC: SetC, -structure ConA { - @required - // conB: ConB, + setD: SetD, - // optConB: ConB, - - // conBList: ConBList, - - conBSet: ConBSet, + setE: SetE +} - // conBMap: ConBMap +structure AnOperationOutput { + set: SetA } -structure ConB { - @required - nice: String, - @required - int: Integer, +set SetA { + member: SetB +} - optNice: String, - optInt: Integer +set SetB { + member: String } -// structure RecursiveShapesInputOutput { -// nested: RecursiveShapesInputOutputNested1 -// } -// -// structure RecursiveShapesInputOutputNested1 { -// @required -// recursiveMember: RecursiveShapesInputOutputNested2 -// } -// -// structure RecursiveShapesInputOutputNested2 { -// @required -// recursiveMember: RecursiveShapesInputOutputNested1, -// } - -// list ValidList { -// member: RecursiveShapesInputOutput -// } -// -// structure RecursiveShapesInputOutput { -// @required -// foo: ValidList -// } - -list ConBList { - member: AnotherList +set SetC { + member: MapA } -list AnotherList { - member: ConB +set SetD { + member: ListA } -set ConBSet { - member: AnotherSet +set SetE { + member: StructureA } -set AnotherSet { - member: String +list ListA { + member: SetA } -map ConBMap { +map MapA { key: String, - value: AnotherMap + value: String } -map AnotherMap { - key: String, - value: ConBList +structure StructureA { + string: String } From d30baadb128267c6ce21f7f07c87d15da24068bc Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 21 Apr 2022 15:49:12 +0200 Subject: [PATCH 023/255] Revert "sets of sets are not allowed, we get WARNINGS, this model tests that" This reverts commit a7fa884a655bb1ba7784158a1d94e0633d083e22. --- codegen-server-test/model/simple.smithy | 83 ++++++++++++++++++------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 50f15fd0a3..fd41e81b71 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -14,54 +14,89 @@ service SimpleService { @http(uri: "/operation", method: "GET") operation AnOperation { + // input: RecursiveShapesInputOutput, + // output: RecursiveShapesInputOutput, input: AnOperationInput, output: AnOperationOutput, } structure AnOperationInput { @required - setA: SetA, - - setC: SetC, - - setD: SetD, - - setE: SetE + conA: ConA } structure AnOperationOutput { - set: SetA + conA: ConA } -set SetA { - member: SetB +structure ConA { + @required + // conB: ConB, + + // optConB: ConB, + + // conBList: ConBList, + + conBSet: ConBSet, + + // conBMap: ConBMap } -set SetB { - member: String +structure ConB { + @required + nice: String, + @required + int: Integer, + + optNice: String, + optInt: Integer } -set SetC { - member: MapA +// structure RecursiveShapesInputOutput { +// nested: RecursiveShapesInputOutputNested1 +// } +// +// structure RecursiveShapesInputOutputNested1 { +// @required +// recursiveMember: RecursiveShapesInputOutputNested2 +// } +// +// structure RecursiveShapesInputOutputNested2 { +// @required +// recursiveMember: RecursiveShapesInputOutputNested1, +// } + +// list ValidList { +// member: RecursiveShapesInputOutput +// } +// +// structure RecursiveShapesInputOutput { +// @required +// foo: ValidList +// } + +list ConBList { + member: AnotherList } -set SetD { - member: ListA +list AnotherList { + member: ConB } -set SetE { - member: StructureA +set ConBSet { + member: AnotherSet } -list ListA { - member: SetA +set AnotherSet { + member: String } -map MapA { +map ConBMap { key: String, - value: String + value: AnotherMap } -structure StructureA { - string: String +map AnotherMap { + key: String, + value: ConBList } From 03b732a4490a6687e8406ef5272d239a4413807d Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 13:13:58 +0200 Subject: [PATCH 024/255] only generate unconstrained shapes if they are reachable from operation input --- codegen-server-test/model/simple.smithy | 18 ++++--- .../server/smithy/ServerCodegenVisitor.kt | 19 ++++--- .../generators/ServerBuilderGenerator.kt | 53 ++++++++++--------- .../ServerHttpBoundProtocolGenerator.kt | 2 +- .../codegen/smithy/generators/Instantiator.kt | 4 +- .../smithy/generators/StructureGenerator.kt | 14 +++-- 6 files changed, 67 insertions(+), 43 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index fd41e81b71..07953c8ec2 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -26,20 +26,22 @@ structure AnOperationInput { } structure AnOperationOutput { - conA: ConA + conA: ConA, + + conCList: ConCList, } structure ConA { @required - // conB: ConB, + conB: ConB, - // optConB: ConB, + optConB: ConB, - // conBList: ConBList, + conBList: ConBList, - conBSet: ConBSet, + // conBSet: ConBSet, - // conBMap: ConBMap + conBMap: ConBMap } structure ConB { @@ -79,6 +81,10 @@ list ConBList { member: AnotherList } +list ConCList { + member: AnotherList +} + list AnotherList { member: ConB } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 955fb35b9b..80538c9a85 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -72,6 +72,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator private val unconstrainedModule = RustModule.private("unconstrained", "Unconstrained types for constrained shapes.") + private val shapesReachableFromOperationInputs: Set init { val symbolVisitorConfig = @@ -102,6 +103,10 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + + val walker = Walker(model) + val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) } + shapesReachableFromOperationInputs = inputShapes.flatMap { walker.walkShapes(it) }.toSet() } /** @@ -178,7 +183,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val builderGenerator = ServerBuilderGenerator(codegenContext, shape) + val builderGenerator = ServerBuilderGenerator( + codegenContext, + shape, + takeInUnconstrainedTypes = shapesReachableFromOperationInputs.contains(shape) + ) builderGenerator.render(writer) writer.implBlock(shape, symbolProvider) { builderGenerator.renderConvenienceMethod(this) @@ -187,9 +196,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun listShape(shape: ListShape) { - // TODO We only have to generate this for lists that are reachable from operation - // input AND from which you can reach a shape that is constrained. - if (shape.canReachConstrainedShape(model, symbolProvider)) { + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") rustCrate.withModule(unconstrainedModule) { writer -> @@ -206,9 +213,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun mapShape(shape: MapShape) { - // TODO We only have to generate this for maps that are reachable from operation - // input AND from which you can reach a shape that is constrained. - if (shape.canReachConstrainedShape(model, symbolProvider)) { + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") rustCrate.withModule(unconstrainedModule) { writer -> diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ba3ccc4c64..fa074c3614 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -46,9 +46,11 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - This one takes in codegenContext. // - Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. // - This builder is not `PartialEq`. +// - Always implements either From for Structure or TryFrom for Structure. class ServerBuilderGenerator( private val codegenContext: CodegenContext, - private val shape: StructureShape + private val shape: StructureShape, + private val takeInUnconstrainedTypes: Boolean = false ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -57,6 +59,7 @@ class ServerBuilderGenerator( private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() + private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) fun render(writer: RustWriter) { writer.docs("See #D.", structureSymbol) @@ -66,7 +69,7 @@ class ServerBuilderGenerator( } private fun renderBuilder(writer: RustWriter) { - if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { + if (isBuilderFallible) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. Attribute.NonExhaustive.render(writer) @@ -101,7 +104,7 @@ class ServerBuilderGenerator( for (member in members) { renderBuilderMemberFn(this, member) - if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { renderBuilderMemberSetterFn(this, member) } } @@ -154,8 +157,7 @@ class ServerBuilderGenerator( } private fun renderBuildFn(implBlockWriter: RustWriter) { - val fallibleBuilder = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider) - val returnType = when (fallibleBuilder) { + val returnType = when (isBuilderFallible) { true -> "Result<${implBlockWriter.format(structureSymbol)}, ConstraintViolation>" false -> implBlockWriter.format(structureSymbol) } @@ -163,7 +165,7 @@ class ServerBuilderGenerator( // TODO Document it returns the first error. implBlockWriter.docs("Consumes the builder and constructs a #D.", structureSymbol) implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { - conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { + conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { coreBuilder(this) } } @@ -203,7 +205,7 @@ class ServerBuilderGenerator( writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val constrainedType = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" if (symbol.isOptional()) { if (hasBox) { @@ -331,7 +333,7 @@ class ServerBuilderGenerator( * Returns the builder failure associated with the `member` field if its target is constrained. */ private fun builderConstraintViolationForMember(member: MemberShape) = - if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { ConstraintViolation(member, ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) } else { null @@ -382,22 +384,25 @@ class ServerBuilderGenerator( * Returns the symbol for a builder's member. * All builder members are optional, but only some are `Option`s where `T` needs to be constrained. */ - private fun builderMemberSymbol(member: MemberShape): Symbol { - val strippedOption = symbolProvider.toSymbol(member) - // Strip the `Option` in case the member is not `required`. - .mapRustType { it.stripOuter() } - - val hadBox = strippedOption.isRustBoxed() - return strippedOption - // Strip the `Box` in case the member can reach itself recursively. - .mapRustType { it.stripOuter() } - // Wrap it in the Cow-like `constrained::Constrained` type in case the target member shape can reach a constrained shape. - .letIf(member.targetCanReachConstrainedShape(model, symbolProvider)) { it.wrapMaybeConstrained() } - // Box it in case the member can reach itself recursively. - .letIf(hadBox) { it.makeRustBoxed() } - // Ensure we always end up with an `Option`. - .makeOptional() - } + private fun builderMemberSymbol(member: MemberShape): Symbol = + if (takeInUnconstrainedTypes) { + val strippedOption = symbolProvider.toSymbol(member) + // Strip the `Option` in case the member is not `required`. + .mapRustType { it.stripOuter() } + + val hadBox = strippedOption.isRustBoxed() + strippedOption + // Strip the `Box` in case the member can reach itself recursively. + .mapRustType { it.stripOuter() } + // Wrap it in the Cow-like `constrained::Constrained` type in case the target member shape can reach a constrained shape. + .letIf(member.targetCanReachConstrainedShape(model, symbolProvider)) { it.wrapMaybeConstrained() } + // Box it in case the member can reach itself recursively. + .letIf(hadBox) { it.makeRustBoxed() } + // Ensure we always end up with an `Option`. + .makeOptional() + } else { + symbolProvider.toSymbol(member).makeOptional() + } /** * Writes the code to instantiate the struct the builder builds. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 54f645daa6..996896b894 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -626,7 +626,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( serverRenderUriPathParser(this, operationShape) serverRenderQueryStringParser(this, operationShape) - val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider)) { + val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider, takeInUnconstrainedTypes = false)) { "?" } else "" rustTemplate("input.build()$err", *codegenScope) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index b0473d9f93..c68bfffaf3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -53,7 +53,7 @@ import software.amazon.smithy.rust.codegen.util.isStreaming /** * Instantiator generates code to instantiate a given Shape given a `Node` representing the value * - * This is primarily used during Protocol test generation + * This is only used during protocol test generation. */ class Instantiator( private val symbolProvider: RustSymbolProvider, @@ -275,7 +275,7 @@ class Instantiator( } writer.write(".build()") // TODO Client - if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider)) { + if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false)) { writer.write(".unwrap()") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index f592b76a01..343db09eb4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -118,13 +118,21 @@ class StructureGenerator( .map { symbolProvider.toSymbol(it) }.any { // If any members are not optional && we can't use a default, we need to // generate a fallible builder. + // TODO Won't canUseDefault suffice? !it.isOptional() && !it.canUseDefault() } // TODO Ensure server subproject uses this function // TODO Not quite right. @box not taken into account. Also shape builders / constrained shapes - fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider) = - structureShape.canReachConstrainedShape(model, symbolProvider) + fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider, takeInUnconstrainedTypes: Boolean) = + if (takeInUnconstrainedTypes) { + structureShape.canReachConstrainedShape(model, symbolProvider) + } else { + structureShape + .members() + .map { symbolProvider.toSymbol(it) } + .any { !it.isOptional() } + } } /** @@ -226,7 +234,7 @@ class StructureGenerator( renderDebugImpl() // TODO This only needs to be called in the server, for structures that are reachable from operation input AND - // that are constrained. It's probably best that we move it to an entirely different class. + // that are constrained. It's probably best that we move it to an entirely different class and unit test it. renderConstrainedTraitImpl() } From 2f913f4eed6c637c37c08784600553de77b7e4e3 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 13:25:59 +0200 Subject: [PATCH 025/255] only generate impl Constrained for shapes if they are reachable from operation input --- .../server/smithy/ServerCodegenVisitor.kt | 5 +++ .../generators/ServerBuilderGenerator.kt | 4 ++- .../ServerStructureConstrainedTraitImpl.kt | 32 +++++++++++++++++++ .../smithy/generators/StructureGenerator.kt | 16 ---------- 4 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 80538c9a85..f9ca49c075 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader @@ -192,6 +193,10 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: writer.implBlock(shape, symbolProvider) { builderGenerator.renderConvenienceMethod(this) } + + if (shapesReachableFromOperationInputs.contains(shape)) { + ServerStructureConstrainedTraitImpl(symbolProvider, shape, writer).render() + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index fa074c3614..36f5ccb93a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -83,7 +83,9 @@ class ServerBuilderGenerator( // TODO This only needs to be generated for operation input shapes. renderImplFromConstraintViolationForRequestRejection(writer) - renderImplFromBuilderForMaybeConstrained(writer) + if (takeInUnconstrainedTypes) { + renderImplFromBuilderForMaybeConstrained(writer) + } renderTryFromBuilderImpl(writer) } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt new file mode 100644 index 0000000000..5be20d9618 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol + +class ServerStructureConstrainedTraitImpl( + private val symbolProvider: RustSymbolProvider, + private val shape: StructureShape, + private val writer: RustWriter +) { + fun render() { + writer.rustTemplate( + """ + impl #{ConstrainedTrait} for #{Structure} { + type Unconstrained = #{Builder}; + } + """, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "Structure" to symbolProvider.toSymbol(shape), + "Builder" to shape.builderSymbol(symbolProvider) + ) + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 343db09eb4..07386c3032 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -175,18 +175,6 @@ class StructureGenerator( } } - private fun renderConstrainedTraitImpl() { - writer.rust( - """ - impl #T for $name { - type Unconstrained = #T; - } - """, - RuntimeType.ConstrainedTrait(), - shape.builderSymbol(symbolProvider) - ) - } - private fun renderStructureImpl() { if (accessorMembers.isEmpty()) { return @@ -232,10 +220,6 @@ class StructureGenerator( renderStructureImpl() renderDebugImpl() - - // TODO This only needs to be called in the server, for structures that are reachable from operation input AND - // that are constrained. It's probably best that we move it to an entirely different class and unit test it. - renderConstrainedTraitImpl() } private fun RustWriter.forEachMember( From d8dd9deb5dad33a01d964636c76a3800bbcbfdf4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 14:35:38 +0200 Subject: [PATCH 026/255] only generate converter from ConstraintViolation into RequestRejection for operation input shapes --- .../server/smithy/generators/ServerBuilderGenerator.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 36f5ccb93a..5c4fa1ff08 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -37,6 +37,7 @@ import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase @@ -50,7 +51,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase class ServerBuilderGenerator( private val codegenContext: CodegenContext, private val shape: StructureShape, - private val takeInUnconstrainedTypes: Boolean = false + private val takeInUnconstrainedTypes: Boolean = false, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -80,8 +81,11 @@ class ServerBuilderGenerator( renderImplDisplayConstraintViolation(writer) writer.rust("impl std::error::Error for ConstraintViolation { }") - // TODO This only needs to be generated for operation input shapes. - renderImplFromConstraintViolationForRequestRejection(writer) + // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is + // an operation input shape. + if (shape.hasTrait()) { + renderImplFromConstraintViolationForRequestRejection(writer) + } if (takeInUnconstrainedTypes) { renderImplFromBuilderForMaybeConstrained(writer) From 78b0a7bd5b21c3ad60d423d0af4eb6202a3fdee6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 14:58:08 +0200 Subject: [PATCH 027/255] add docs for server builder build() --- .../generators/ServerBuilderGenerator.kt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 5c4fa1ff08..267e11306a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -72,7 +72,7 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) - // TODO(): `#[non_exhaustive] until we commit to making builders of builders public. + // TODO(): `#[non_exhaustive] if/until we commit to making builders of builders public. Attribute.NonExhaustive.render(writer) writer.rustBlock("pub enum ConstraintViolation") { constraintViolations().forEach { renderConstraintViolation(this, it) } @@ -167,9 +167,19 @@ class ServerBuilderGenerator( true -> "Result<${implBlockWriter.format(structureSymbol)}, ConstraintViolation>" false -> implBlockWriter.format(structureSymbol) } - // TODO Document when builder can fail. - // TODO Document it returns the first error. - implBlockWriter.docs("Consumes the builder and constructs a #D.", structureSymbol) + implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) + if (isBuilderFallible) { + implBlockWriter.docs( + """ + The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. + """, + structureSymbol + ) + + if (constraintViolations().size > 1) { + implBlockWriter.docs("If the builder fails, it will return the _first_ encountered [`ConstraintViolation`].") + } + } implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { coreBuilder(this) From c05ef08a31ca5fa3885b0bd1ad841c36ec539cbc Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 15:03:38 +0200 Subject: [PATCH 028/255] remove useless allow(dead_code) --- .../smithy/generators/ServerBuilderGenerator.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 267e11306a..3877b09386 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -72,7 +72,7 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) - // TODO(): `#[non_exhaustive] if/until we commit to making builders of builders public. + // TODO(): `#[non_exhaustive] unless we commit to making builders of builders public. Attribute.NonExhaustive.render(writer) writer.rustBlock("pub enum ConstraintViolation") { constraintViolations().forEach { renderConstraintViolation(this, it) } @@ -248,6 +248,8 @@ class ServerBuilderGenerator( /** * Render a `set_foo` method. This method is able to take in builders of structure shape types. + * + * This method is only used by deserializers at the moment and is therefore `pub(crate)`. */ private fun renderBuilderMemberSetterFn( writer: RustWriter, @@ -258,10 +260,7 @@ class ServerBuilderGenerator( val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) - // TODO: This method is only used by deserializers, so it will remain unused for shapes that are not (transitively) - // part of an operation input. We therefore `[allow(dead_code)]` here. - Attribute.AllowDeadCode.render(writer) - // TODO(): `pub(crate)` until we commit to making builders of builders public. + // TODO: `pub(crate)` unless we commit to making builders of builders public. // Setter names will never hit a reserved word and therefore never need escaping. writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { rust( @@ -303,7 +302,7 @@ class ServerBuilderGenerator( private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) { if (constraintViolation.kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) { - // TODO(): `#[doc(hidden)]` until we commit to making builders of builders public. + // TODO(): `#[doc(hidden)]` unless we commit to making builders of builders public. Attribute.DocHidden.render(writer) } From 9ad6ee1e686a73902bc4f5f1d9bef3afb49ee048 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 17:02:05 +0200 Subject: [PATCH 029/255] some rustdocs for ConstraintViolation --- .../generators/ServerBuilderGenerator.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 3877b09386..c03210e297 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -73,6 +73,7 @@ class ServerBuilderGenerator( if (isBuilderFallible) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) // TODO(): `#[non_exhaustive] unless we commit to making builders of builders public. + writer.docs("Holds one variant for each of the ways the builder can fail.") Attribute.NonExhaustive.render(writer) writer.rustBlock("pub enum ConstraintViolation") { constraintViolations().forEach { renderConstraintViolation(this, it) } @@ -300,16 +301,12 @@ class ServerBuilderGenerator( fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE } - private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) { - if (constraintViolation.kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) { - // TODO(): `#[doc(hidden)]` unless we commit to making builders of builders public. - Attribute.DocHidden.render(writer) - } - - // TODO Add Rust docs. - + private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) = when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> writer.rust("${constraintViolation.name()},") + ConstraintViolationKind.MISSING_MEMBER -> { + writer.docs("${constraintViolationMessage(constraintViolation).capitalize()}.") + writer.rust("${constraintViolation.name()},") + } ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { val targetShape = model.expectShape(constraintViolation.forMember.target) @@ -322,18 +319,22 @@ class ServerBuilderGenerator( // Note we cannot express the inner constraint violation as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. + writer.docs("${constraintViolationMessage(constraintViolation)}.") + // TODO: `#[doc(hidden)]` unless we commit to making builders of builders public. + Attribute.DocHidden.render(writer) writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } } - } + /** + * A message for a `ConstraintViolation` variant. This is used in both Rust documentation and the `Display` trait implementation. + */ private fun constraintViolationMessage(constraintViolation: ConstraintViolation): String { val memberName = symbolProvider.toMemberName(constraintViolation.forMember) - // TODO $structureSymbol here is not quite right because it's printing the full namespace: crate:: in the context of the user will surely be different. return when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not specified but it is required when building `$structureSymbol`" + ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" // TODO Nest errors. - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `$structureSymbol`" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" } } From 0d2658dc1006d317759b7c777d04c65eb298a545 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 17:07:49 +0200 Subject: [PATCH 030/255] comments --- .../smithy/generators/ServerBuilderGenerator.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index c03210e297..ff75b383b1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -310,9 +310,9 @@ class ServerBuilderGenerator( ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { val targetShape = model.expectShape(constraintViolation.forMember.target) - // TODO I guess the RustBoxTrait logic could be handled by the symbol provider. val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape) + // If the corresponding structure's member is boxed, box this constraint violation symbol too. .letIf(constraintViolation.forMember.hasTrait()) { it.makeRustBoxed() } @@ -359,8 +359,8 @@ class ServerBuilderGenerator( * Returns the builder failure associated with the `member` field if it is `required`. */ private fun builderMissingFieldForMember(member: MemberShape) = - // TODO: We go through the symbol provider because non-`required` blob streaming members are interpreted as `required`, - // so we can't use `member.isOptional`. See https://github.com/awslabs/smithy-rs/issues/1302. + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): We go through the symbol provider because + // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. if (symbolProvider.toSymbol(member).isOptional()) { null } else { @@ -368,7 +368,7 @@ class ServerBuilderGenerator( } private fun renderTryFromBuilderImpl(writer: RustWriter) { - // TODO `TryFrom` is in Rust 2021's prelude. + // TODO(https://github.com/awslabs/smithy-rs/issues/1332) `TryFrom` is in Rust 2021's prelude. writer.rustTemplate( """ impl std::convert::TryFrom for #{Structure} { @@ -431,7 +431,7 @@ class ServerBuilderGenerator( * a) `Option`; or * b) `T`. * - * For each member, this function first unwraps case 1. into 2., and then converts into b) if necessary. + * For each member, this function first safely unwraps case 1. into 2., and then converts into b) if necessary. */ private fun coreBuilder(writer: RustWriter) { writer.rustBlock("#T", structureSymbol) { @@ -441,11 +441,11 @@ class ServerBuilderGenerator( withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). builderConstraintViolationForMember(member)?.also { constraintViolation -> - // TODO Remove `TryInto` import when we switch to 2021 edition. val hasBox = builderMemberSymbol(member) .mapRustType { it.stripOuter() } .isRustBoxed() if (hasBox) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1332) `TryInto` is in Rust 2021's prelude. rustTemplate( """ .map(|v| match *v { From 951195cecfcf0962ac6bf5d6eb342e36bbc190cc Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 17:19:49 +0200 Subject: [PATCH 031/255] moar --- .../generators/UnconstrainedListGenerator.kt | 11 +++-------- .../generators/UnconstrainedMapGenerator.kt | 15 ++++++--------- .../smithy/rust/codegen/smithy/Constraints.kt | 10 ++-------- 3 files changed, 11 insertions(+), 25 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index 5b768b4fcd..810a042377 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained // TODO Docs -// TODO Can we reuse this generator for sets? class UnconstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, @@ -30,24 +29,20 @@ class UnconstrainedListGenerator( fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - // TODO Unit test that this is pub(crate). - - // TODO Some of these can become private properties. val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() val name = symbol.name val innerShape = model.expectShape(shape.member.target) val innerSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) - // TODO: We will need a `ConstrainedSymbolProvider` when we have constraint traits. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199): We will need a `ConstrainedSymbolProvider` when we have constraint traits. val constrainedSymbol = symbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - // TODO Strictly, `ConstrainedTrait` only needs to be implemented if this list is a struct member. - // TODO The implementation of the Constrained trait is probably not for the correct type. There might be more than + // TODO The implementation of the `Constrained` trait is probably not for the correct type. There might be more than // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. - // So we might have to newtype things. + // So we will have to newtype things. writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index cadef12b9d..2f44307f56 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -33,9 +33,7 @@ class UnconstrainedMapGenerator( ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - // TODO Unit test that this is pub(crate). - // TODO Some of these can become private properties. val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() val name = symbol.name @@ -51,7 +49,7 @@ class UnconstrainedMapGenerator( } else { symbolProvider.toSymbol(valueShape) } - // TODO: We will need a `ConstrainedSymbolProvider` when we have constraint traits. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199): We will need a `ConstrainedSymbolProvider` when we have constraint traits. val constrainedSymbol = symbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val constraintViolationCodegenScope = listOfNotNull( @@ -67,11 +65,10 @@ class UnconstrainedMapGenerator( }, ).toTypedArray() - // TODO Strictly, `ConstrainedTrait` only needs to be implemented if this list is a struct member. - // TODO The implementation of the Constrained trait is probably not for the correct type. There might be more than - // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint - // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. - // So we might have to newtype things. + // TODO The implementation of the `Constrained` trait is probably not for the correct type. There might be more than + // one "path" to an e.g. HashMap> with different constraint traits along the path, because constraint + // traits can be applied to members, or simply because the model might have two different maps holding `StructA`. + // So we will have to newtype things. writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { rustTemplate( """ @@ -127,7 +124,7 @@ class UnconstrainedMapGenerator( is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - // TODO Constraint traits on simple shapes. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. else -> false } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 2ff68a432a..fff757d2a8 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -13,8 +13,7 @@ import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.rust.codegen.util.hasTrait -// TODO Find a better place for these primitives. Probably rust.codegen.util -// TODO Unit test these functions. +// TODO Unit test these functions and then refactor to use a `Walker` instead of hand-rolling our own DFS. // TODO This will work fine if we include RequiredTrait too won't it? fun Shape.hasConstraintTrait() = @@ -77,18 +76,13 @@ private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Mode return false } -// // TODO -// if (shape.id.name == "RecursiveShapesInputOutputNested1") { -// return false -// } - val newVisited = setOf(shape).plus(visited) return when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) - // TODO Constraint traits on simple shapes. + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. else -> false } } \ No newline at end of file From 965dea7ca0873b2c17a8c5aa217f4c2f2ed9575a Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 17:24:46 +0200 Subject: [PATCH 032/255] RequiredTrait part of hasConstraintTrait --- .../amazon/smithy/rust/codegen/smithy/Constraints.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index fff757d2a8..110e5c80a5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -11,13 +11,14 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Unit test these functions and then refactor to use a `Walker` instead of hand-rolling our own DFS. -// TODO This will work fine if we include RequiredTrait too won't it? fun Shape.hasConstraintTrait() = - this.hasTrait() || + this.hasTrait() || + this.hasTrait() || this.hasTrait() || // `uniqueItems` is deprecated, so we ignore it. // this.hasTrait() || From 428630475237a6821043262db8503df6fac9aa0b Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 22 Apr 2022 18:26:00 +0200 Subject: [PATCH 033/255] comments --- .../rust/codegen/server/smithy/testutil/ServerTestHelpers.kt | 2 +- .../server/smithy/UnconstrainedShapeSymbolProviderTest.kt | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index b9626720ac..10dcafe34e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -47,7 +47,7 @@ fun serverTestCodegenContext( model, serverTestSymbolProvider(model), TestRuntimeConfig, - // TODO We should not fabricate a service shape out of thin air here, but rather look it up in the model. + // TODO(https://github.com/awslabs/smithy-rs/pull/1340) We should not fabricate a service shape out of thin air here, but rather look it up in the model. serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), settings, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index e547fa2444..acd3b10580 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -18,6 +18,9 @@ import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup +// TODO I can't move this file to `codegen` subproject because `serverTestSymbolProvider` is only in the `codegen-server` +// subproject, but I need `UnconstrainedSymbolProvider` to be in the `codegen` subproject because it's used in the +// `JsonParserGenerator`. class UnconstrainedShapeSymbolProviderTest { private val baseModelString = """ @@ -59,8 +62,6 @@ class UnconstrainedShapeSymbolProviderTest { """.asSmithyModel() val serviceShape = model.lookup("test#TestService") - // TODO I can't move this file to `codegen` subproject because `serverTestSymbolProvider` is only in server subproject, - // but I need the symbol provider to be in `codegen` subproject because it's used in the JsonParser. val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) val listAShape = model.lookup("test#ListA") From d73cab6be060f52c8299082d771b200d02b6ab89 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 13:16:01 +0200 Subject: [PATCH 034/255] merge fixes --- .../server/smithy/generators/UnconstrainedListGenerator.kt | 3 ++- .../server/smithy/generators/UnconstrainedMapGenerator.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index 810a042377..f149a67584 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -43,7 +44,7 @@ class UnconstrainedListGenerator( // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. // So we will have to newtype things. - writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ ##[derive(Debug, Clone)] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 2f44307f56..a565c7d339 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -69,7 +70,7 @@ class UnconstrainedMapGenerator( // one "path" to an e.g. HashMap> with different constraint traits along the path, because constraint // traits can be applied to members, or simply because the model might have two different maps holding `StructA`. // So we will have to newtype things. - writer.withModule(module, RustMetadata(public = false, pubCrate = true)) { + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ ##[derive(Debug, Clone)] From 00182e3c7a5f557170c97d2fc681d67f66509157 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 13:35:15 +0200 Subject: [PATCH 035/255] Don't use BuildV2 --- .../smithy/generators/ServerBuilderGenerator.kt | 2 +- .../smithy/rust/codegen/testutil/TestHelpers.kt | 2 +- rust-runtime/aws-smithy-http-server/src/rejection.rs | 12 ++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ff75b383b1..38f22eac3b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -142,7 +142,7 @@ class ServerBuilderGenerator( """ impl From for #{RequestRejection} { fn from(value: ConstraintViolation) -> Self { - Self::BuildV2(value.into()) + Self::Build(value.into()) } } """, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt index 3250f3e4eb..68d8ecdc00 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt @@ -80,7 +80,7 @@ fun testCodegenContext( model, testSymbolProvider(model), TestRuntimeConfig, - // TODO We should not fabricate a service shape out of thin air here, but rather look it up in the model. + // TODO(https://github.com/awslabs/smithy-rs/pull/1340) We should not fabricate a service shape out of thin air here, but rather look it up in the model. serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), settings, diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 3cf209afb6..9386e5e359 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -181,13 +181,10 @@ pub enum RequestRejection { FloatParse(crate::Error), BoolParse(crate::Error), - // TODO(https://github.com/awslabs/smithy-rs/issues/1243): In theory, we could get rid of this - // error, but it would be a lot of effort for comparatively low benefit. - /// Used when consuming the input struct builder. - Build(crate::Error), - - // TODO Use the above one. - BuildV2(Box), + /// Used when consuming the input struct builder, and a constraint violation occurs. + // This is the only error that doesn't take in `crate::Error`, since it is constructed directly + // in the code-generated SDK instead of in this crate. + Build(Box), } impl std::error::Error for RequestRejection {} @@ -200,7 +197,6 @@ impl std::error::Error for RequestRejection {} convert_to_request_rejection!(aws_smithy_json::deserialize::Error, JsonDeserialize); convert_to_request_rejection!(aws_smithy_xml::decode::XmlError, XmlDeserialize); -convert_to_request_rejection!(aws_smithy_http::operation::BuildError, Build); convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); From 0d695c3209b1bcd9d2f6727fb63a93754baad952 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 13:39:32 +0200 Subject: [PATCH 036/255] Rename to hasFallibleBuilder --- .../codegen/smithy/generators/BuilderGenerator.kt | 2 +- .../codegen/smithy/generators/StructureGenerator.kt | 13 +++++++++---- .../smithy/protocols/HttpBoundProtocolGenerator.kt | 2 +- .../parse/XmlBindingTraitParserGenerator.kt | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index 378bc7fa3b..17bbc99c2e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -76,7 +76,7 @@ class BuilderGenerator( } private fun renderBuildFn(implBlockWriter: RustWriter) { - val fallibleBuilder = StructureGenerator.fallibleBuilder(shape, symbolProvider) + val fallibleBuilder = StructureGenerator.hasFallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 07386c3032..396373c5ad 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -108,9 +108,11 @@ class StructureGenerator( } companion object { - /** Returns whether a structure shape requires a fallible builder to be generated. */ - // TODO Rename to `hasFallibleBuilder`. - fun fallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = + /** + * Returns whether a structure shape, whose builder has been generated with [BuilderGenerator], requires a + * fallible builder to be constructed. + */ + fun hasFallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = // All operation inputs should have fallible builders in case a new required field is added in the future. structureShape.hasTrait() || structureShape @@ -122,8 +124,11 @@ class StructureGenerator( !it.isOptional() && !it.canUseDefault() } - // TODO Ensure server subproject uses this function // TODO Not quite right. @box not taken into account. Also shape builders / constrained shapes + /** + * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a + * fallible builder to be constructed. + */ fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider, takeInUnconstrainedTypes: Boolean) = if (takeInUnconstrainedTypes) { structureShape.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/HttpBoundProtocolGenerator.kt index 6ff23c4319..3407fe46e0 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -311,7 +311,7 @@ class HttpBoundProtocolTraitImplGenerator( } } - val err = if (StructureGenerator.fallibleBuilder(outputShape, symbolProvider)) { + val err = if (StructureGenerator.hasFallibleBuilder(outputShape, symbolProvider)) { ".map_err(${format(errorSymbol)}::unhandled)?" } else "" rust("output.build()$err") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 9aa5ee0f1c..8cddc6436f 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -476,7 +476,7 @@ class XmlBindingTraitParserGenerator( rust("let _ = decoder;") } withBlock("Ok(builder.build()", ")") { - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { + if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { // NOTE:(rcoh) This branch is unreachable given the current nullability rules. // Only synthetic inputs can have fallible builders, but synthetic inputs can never be parsed // (because they're inputs, only outputs will be parsed!) From 7cbfaabd384656909a4fef6a0f474c047d432c71 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 14:16:46 +0200 Subject: [PATCH 037/255] move targetCanReachConstrainedShape --- .../smithy/generators/ServerBuilderGenerator.kt | 2 +- .../amazon/smithy/rust/codegen/smithy/Constraints.kt | 11 +++++++++++ .../codegen/smithy/generators/StructureGenerator.kt | 10 ---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 38f22eac3b..292959756d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -29,7 +29,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.generators.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -37,6 +36,7 @@ import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 110e5c80a5..4c9e6c3af4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -5,6 +5,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape 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.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape @@ -70,6 +71,16 @@ fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvid return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider, visited) } +// TODO Perhaps move these into `StructureGenerator`? +fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + when (val targetShape = model.expectShape(this.target)) { + is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) + is SetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) + is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) + is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) + else -> false + } + private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model, symbolProvider: SymbolProvider, visited: Set): Boolean { check(!shape.isConstrained(symbolProvider)) { "This function can only be called with unconstrained shapes" } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 396373c5ad..dfac5888f5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -56,16 +56,6 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } } -// TODO Perhaps move these into `StructureGenerator`? -fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - when (val targetShape = model.expectShape(this.target)) { - is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) - is SetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) - is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) - is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) - else -> false - } - /** * The name of the builder's setter the server deserializer should use. * Setter names will never hit a reserved word and therefore never need escaping. From 75643ab3d760619f4d90a3774a7e44b0eff80e86 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 14:57:53 +0200 Subject: [PATCH 038/255] Make JsonParserGenerator work with client again; cst passes --- .../ServerHttpBoundProtocolGenerator.kt | 14 ++++++++----- .../smithy/generators/StructureGenerator.kt | 11 ++++++---- .../protocols/parse/JsonParserGenerator.kt | 20 +++++++++++-------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 996896b894..218b021cad 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -604,7 +604,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rust( """ { - input = input.${member.deserializerBuilderSetterName(model, symbolProvider)}(${ + input = input.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(${ if (symbolProvider.toSymbol(binding.member).isOptional()) { "Some(value)" } else { @@ -762,7 +762,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val deserializer = generateParsePercentEncodedStrFn(binding) rustTemplate( """ - input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider)}( + input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider, mode)}( #{deserializer}(m$index)? ); """.trimIndent(), @@ -859,7 +859,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !seen_$memberName && k == "${it.locationName}" { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider)}( + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, mode)}( #{deserializer}(&v)? ); seen_$memberName = true; @@ -932,7 +932,11 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } if (queryParamsBinding != null) { - rust("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider)}(${ + rust("input = input.${queryParamsBinding.member.deserializerBuilderSetterName( + model, + symbolProvider, + mode + )}(${ if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { "Some(query_params)" } else { @@ -945,7 +949,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !$memberName.is_empty() { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider)}(${ + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, mode)}(${ if (symbolProvider.toSymbol(it.member).isOptional()) { "Some($memberName)" } else { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index dfac5888f5..f6ce40fd30 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -9,10 +9,8 @@ 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.CollectionShape -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.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait @@ -27,6 +25,7 @@ import software.amazon.smithy.rust.codegen.rustlang.isDeref import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -57,10 +56,14 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } /** - * The name of the builder's setter the server deserializer should use. + * The name of the builder's setter the deserializer should use. * Setter names will never hit a reserved word and therefore never need escaping. */ -fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: SymbolProvider): String { +fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: SymbolProvider, mode: CodegenMode): String { + if (mode == CodegenMode.Client) { + return this.setterName() + } + val canReachConstrainedShape = when (val targetShape = model.expectShape(this.target)) { is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) is MapShape -> targetShape.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 304e0c31a4..6d167a5416 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -34,6 +34,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -42,6 +43,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant +import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation @@ -210,8 +212,8 @@ class JsonParserGenerator( rustBlock("match key.to_unescaped()?.as_ref()") { for (member in members) { rustBlock("${jsonName(member).dq()} =>") { - if (member.isOptional) { - withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider)}(", ");") { + if (symbolProvider.toSymbol(member).isOptional()) { + withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(", ");") { deserializeMember(member) } } else { @@ -219,7 +221,7 @@ class JsonParserGenerator( deserializeMember(member) rust(""" { - builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider)}(v); + builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(v); } """) } @@ -246,8 +248,10 @@ class JsonParserGenerator( } val symbol = symbolProvider.toSymbol(memberShape) if (symbol.isRustBoxed()) { - // Before boxing, convert into `Validated`. - rust(".map(|x| x.into())") + if (mode == CodegenMode.Server) { + // Before boxing, convert into `Validated`. + rust(".map(|x| x.into())") + } rust(".map(Box::new)") } } @@ -285,7 +289,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeCollection(shape: CollectionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = shape.canReachConstrainedShape(model, symbolProvider) + val returnUnconstrainedType = mode == CodegenMode.Server && shape.canReachConstrainedShape(model, symbolProvider) val returnType = if (returnUnconstrainedType) { unconstrainedShapeSymbolProvider.toSymbol(shape) } else { @@ -341,7 +345,7 @@ class JsonParserGenerator( val keyTarget = model.expectShape(shape.key.target) as StringShape val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = shape.canReachConstrainedShape(model, symbolProvider) + val returnUnconstrainedType = mode == CodegenMode.Server && shape.canReachConstrainedShape(model, symbolProvider) val returnType = if (returnUnconstrainedType) { unconstrainedShapeSymbolProvider.toSymbol(shape) } else { @@ -390,7 +394,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeStruct(shape: StructureShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) - val returnBuilder = shape.canReachConstrainedShape(model, symbolProvider) + val returnBuilder = mode == CodegenMode.Server && shape.canReachConstrainedShape(model, symbolProvider) val returnType = if (returnBuilder) { unconstrainedShapeSymbolProvider.toSymbol(shape) } else { From 32809dd3bc4d525554315f1b15b20b0512aa5cd7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 15:30:45 +0200 Subject: [PATCH 039/255] Make Instantiator and BuilderGenerator work again with the client; crt works --- .../codegen/smithy/generators/BuilderGenerator.kt | 14 +++++++++++--- .../codegen/smithy/generators/Instantiator.kt | 15 ++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index 17bbc99c2e..d98fc3eda1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asArgument +import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape @@ -129,16 +130,23 @@ class BuilderGenerator( } } + /** + * Render a `set_foo` method. This is useful as a target for code generation, because the argument type + * is the same as the resulting member type, and is always optional. + */ private fun renderBuilderMemberSetterFn( writer: RustWriter, outerType: RustType, member: MemberShape, memberName: String ) { - // Render a `set_foo` method. This is useful as a target for code generation, because the argument type - // is the same as the resulting member type, and is always optional. + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): This `asOptional()` call is superfluous except in + // the case where the shape is a `@streaming` blob, because [StreamingTraitSymbolProvider] always generates + // a non `Option`al target type: in all other cases the client generates `Option`al types. + val inputType = outerType.asOptional() + writer.documentShape(member, model) - writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${outerType.render()}) -> Self") { + writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") { rust("self.$memberName = input; self") } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index c68bfffaf3..c458dffaf5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -157,9 +157,12 @@ class Instantiator( } else { writer.conditionalBlock( "Some(", ")", -// conditional = ctx.builder || symbol.isOptional() - // TODO Client builder - conditional = symbol.isOptional() + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): This `ctx.builder` condition is superfluous + // except in the case where the shape is a `@streaming` blob, because [StreamingTraitSymbolProvider] + // always generates a non `Option`al target type: in all other cases the client generates `Option`al + // types. + conditional = ctx.builder || symbol.isOptional() +// conditional = symbol.isOptional() ) { writer.conditionalBlock( "Box::new(", @@ -269,13 +272,15 @@ class Instantiator( data.members.forEach { (key, value) -> val memberShape = shape.expectMember(key.value) // TODO Client uses setter name. - writer.withBlock(".${symbolProvider.toMemberName(memberShape)}(", ")") { + writer.withBlock(".${memberShape.setterName()}(", ")") { +// writer.withBlock(".${symbolProvider.toMemberName(memberShape)}(", ")") { renderMember(this, memberShape, value, ctx) } } writer.write(".build()") // TODO Client - if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false)) { + if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { +// if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false)) { writer.write(".unwrap()") } } From f7f214897ebfe10b017c7f66e26ed72ad89d7289 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 16:36:06 +0200 Subject: [PATCH 040/255] Make sst and srt work again --- .../protocol/ServerProtocolTestGenerator.kt | 3 +- .../ServerHttpBoundProtocolGenerator.kt | 18 ++++++---- .../codegen/smithy/generators/Instantiator.kt | 34 +++++++++++-------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 989c91b561..7247bb0ff8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -36,6 +36,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.testutil.TokioTest @@ -72,7 +73,7 @@ class ServerProtocolTestGenerator( private val operationErrorName = "crate::error::${operationSymbol.name}Error" private val instantiator = with(codegenContext) { - Instantiator(symbolProvider, model, runtimeConfig) + Instantiator(symbolProvider, model, runtimeConfig, forWhom = CodegenTarget.SERVER) } private val codegenScope = arrayOf( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 218b021cad..43ba542cb7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -604,7 +604,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rust( """ { - input = input.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(${ + input = input.${member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(${ if (symbolProvider.toSymbol(binding.member).isOptional()) { "Some(value)" } else { @@ -762,10 +762,16 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val deserializer = generateParsePercentEncodedStrFn(binding) rustTemplate( """ - input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider, mode)}( + input = input.${ + binding.member.deserializerBuilderSetterName( + model, + symbolProvider, + codegenContext.mode + ) + }( #{deserializer}(m$index)? ); - """.trimIndent(), + """, *codegenScope, "deserializer" to deserializer, ) @@ -859,7 +865,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !seen_$memberName && k == "${it.locationName}" { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, mode)}( + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}( #{deserializer}(&v)? ); seen_$memberName = true; @@ -935,7 +941,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rust("input = input.${queryParamsBinding.member.deserializerBuilderSetterName( model, symbolProvider, - mode + codegenContext.mode )}(${ if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { "Some(query_params)" @@ -949,7 +955,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !$memberName.is_empty() { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, mode)}(${ + input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(${ if (symbolProvider.toSymbol(it.member).isOptional()) { "Some($memberName)" } else { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index c458dffaf5..8011ddb0ea 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -58,7 +58,8 @@ import software.amazon.smithy.rust.codegen.util.isStreaming class Instantiator( private val symbolProvider: RustSymbolProvider, private val model: Model, - private val runtimeConfig: RuntimeConfig + private val runtimeConfig: RuntimeConfig, + private val forWhom: CodegenTarget = CodegenTarget.CLIENT ) { data class Ctx( // The Rust HTTP library lower cases headers but Smithy protocol tests @@ -157,12 +158,14 @@ class Instantiator( } else { writer.conditionalBlock( "Some(", ")", - // TODO(https://github.com/awslabs/smithy-rs/issues/1302): This `ctx.builder` condition is superfluous - // except in the case where the shape is a `@streaming` blob, because [StreamingTraitSymbolProvider] - // always generates a non `Option`al target type: in all other cases the client generates `Option`al - // types. - conditional = ctx.builder || symbol.isOptional() -// conditional = symbol.isOptional() + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The `ctx.builder` condition in the case of + // the client is superfluous except in the case where the shape is a `@streaming` blob, because + // [StreamingTraitSymbolProvider] always generates a non `Option`al target type: in all other cases the + // client generates `Option`al types. + conditional = when (forWhom) { + CodegenTarget.CLIENT -> ctx.builder || symbol.isOptional() + CodegenTarget.SERVER -> symbol.isOptional() + } ) { writer.conditionalBlock( "Box::new(", @@ -266,21 +269,24 @@ class Instantiator( * MyStruct::builder().field_1("hello").field_2(5).build() * ``` */ - // TODO I wonder if we can create a renderStructure that does not use the builder. private fun renderStructure(writer: RustWriter, shape: StructureShape, data: ObjectNode, ctx: Ctx) { writer.write("#T::builder()", symbolProvider.toSymbol(shape)) data.members.forEach { (key, value) -> val memberShape = shape.expectMember(key.value) - // TODO Client uses setter name. - writer.withBlock(".${memberShape.setterName()}(", ")") { -// writer.withBlock(".${symbolProvider.toMemberName(memberShape)}(", ")") { + val setterName = when (forWhom) { + CodegenTarget.CLIENT -> memberShape.setterName() + CodegenTarget.SERVER -> symbolProvider.toMemberName(memberShape) + } + writer.withBlock(".$setterName(", ")") { renderMember(this, memberShape, value, ctx) } } writer.write(".build()") - // TODO Client - if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { -// if (StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false)) { + val hasFallibleBuilder = when (forWhom) { + CodegenTarget.CLIENT -> StructureGenerator.hasFallibleBuilder(shape, symbolProvider) + CodegenTarget.SERVER -> StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false) + } + if (hasFallibleBuilder) { writer.write(".unwrap()") } } From 5271e2e203577ef001f4ef730c47db18e6f97aa4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 16:38:41 +0200 Subject: [PATCH 041/255] run ktlint -F --- .../generators/ServerBuilderGenerator.kt | 10 ++-- .../ServerHttpBoundProtocolGenerator.kt | 48 ++++++++++--------- .../smithy/rust/codegen/rustlang/RustTypes.kt | 2 +- .../smithy/rust/codegen/smithy/Constraints.kt | 12 ++--- .../protocols/parse/JsonParserGenerator.kt | 6 ++- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 292959756d..81eda63ee1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -267,11 +267,11 @@ class ServerBuilderGenerator( rust( """ self.$memberName = ${ - if (member.isOptional) { - "input.map(|v| v.into())" - } else { - "Some(input.into())" - } + if (member.isOptional) { + "input.map(|v| v.into())" + } else { + "Some(input.into())" + } }; self """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 43ba542cb7..12307426ec 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -605,12 +605,12 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ { input = input.${member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(${ - if (symbolProvider.toSymbol(binding.member).isOptional()) { - "Some(value)" - } else { - "value" - } - }); + if (symbolProvider.toSymbol(binding.member).isOptional()) { + "Some(value)" + } else { + "value" + } + }); } """ ) @@ -763,11 +763,11 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ input = input.${ - binding.member.deserializerBuilderSetterName( - model, - symbolProvider, - codegenContext.mode - ) + binding.member.deserializerBuilderSetterName( + model, + symbolProvider, + codegenContext.mode + ) }( #{deserializer}(m$index)? ); @@ -938,17 +938,19 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } if (queryParamsBinding != null) { - rust("input = input.${queryParamsBinding.member.deserializerBuilderSetterName( - model, - symbolProvider, - codegenContext.mode - )}(${ + rust( + "input = input.${queryParamsBinding.member.deserializerBuilderSetterName( + model, + symbolProvider, + codegenContext.mode + )}(${ if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { "Some(query_params)" } else { "query_params" } - });") + });" + ) } queryBindingsTargettingCollection.forEach { val memberName = symbolProvider.toMemberName(it.member) @@ -956,12 +958,12 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ if !$memberName.is_empty() { input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(${ - if (symbolProvider.toSymbol(it.member).isOptional()) { - "Some($memberName)" - } else { - memberName - } - }); + if (symbolProvider.toSymbol(it.member).isOptional()) { + "Some($memberName)" + } else { + memberName + } + }); } """.trimIndent() ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index 7e2126eab4..2e4ebbfc6a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -104,7 +104,7 @@ sealed class RustType { } } - data class MaybeConstrained(override val member: RustType): RustType(), Container { + data class MaybeConstrained(override val member: RustType) : RustType(), Container { val runtimeType: RuntimeType = RuntimeType.MaybeConstrained() override val name = runtimeType.name!! override val namespace = runtimeType.namespace diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 4c9e6c3af4..faf994f614 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -19,11 +19,11 @@ import software.amazon.smithy.rust.codegen.util.hasTrait fun Shape.hasConstraintTrait() = this.hasTrait() || - this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() + this.hasTrait() || + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { @@ -97,4 +97,4 @@ private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Mode // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. else -> false } -} \ No newline at end of file +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 6d167a5416..2d40db8ad3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -219,11 +219,13 @@ class JsonParserGenerator( } else { rust("if let Some(v) = ") deserializeMember(member) - rust(""" + rust( + """ { builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(v); } - """) + """ + ) } } } From cc2b0b720a58bd4d620cdf367ad2cdf2ab8b6b05 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 16:41:14 +0200 Subject: [PATCH 042/255] fix client unit tests --- .../smithy/rust/codegen/generators/StructureGeneratorTest.kt | 3 ++- .../codegen/smithy/generators/EndpointTraitBindingsTest.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt index df5f22247f..4cd4801568 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.raw import software.amazon.smithy.rust.codegen.rustlang.rust @@ -180,7 +181,7 @@ class StructureGeneratorTest { .withModule( "model", // By attaching this lint, any missing documentation becomes a compiler error - RustMetadata(additionalAttributes = listOf(Attribute.Custom("deny(missing_docs)")), public = true) + RustMetadata(additionalAttributes = listOf(Attribute.Custom("deny(missing_docs)")), visibility = Visibility.PUBLIC) ) { StructureGenerator(model, provider, this, model.lookup("com.test#Inner")).render() StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct")).render() diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt index 8780f02b2a..6a14038e24 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.smithy.CodegenContext @@ -61,7 +62,7 @@ internal class EndpointTraitBindingsTest { operationShape.expectTrait(EndpointTrait::class.java) ) val project = TestWorkspace.testProject() - project.withModule(RustModule.default("test", false)) { + project.withModule(RustModule.default("test", visibility = Visibility.PRIVATE)) { it.rust( """ struct GetStatusInput { From 1fd9c745c97643c14477550daf3dcb2b50256b23 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 18:03:54 +0200 Subject: [PATCH 043/255] add note about EventStreamTestTools being incorrectly used for server codegen target --- .../rust/codegen/smithy/protocols/EventStreamTestTools.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt index 19f9b4979f..e69d3ed80d 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt @@ -303,7 +303,9 @@ object EventStreamTestModels { """.trimIndent(), ) { Ec2QueryProtocol(it) }, - ).flatMap { listOf(it, it.copy(mode = CodegenMode.Server)) } + ) + // TODO This is wrong: server tests should be run from the server subproject, and use `serverTestSymbolProvider()` + // .flatMap { listOf(it, it.copy(mode = CodegenMode.Server)) } class UnmarshallTestCasesProvider : ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream = From 3d18e88aafec1a70db0c9a8b07591bbaf430f894 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 19:11:45 +0200 Subject: [PATCH 044/255] Add copyright notice to constrained.rs --- rust-runtime/inlineable/src/constrained.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust-runtime/inlineable/src/constrained.rs b/rust-runtime/inlineable/src/constrained.rs index c74ac4a456..7ce2db1059 100644 --- a/rust-runtime/inlineable/src/constrained.rs +++ b/rust-runtime/inlineable/src/constrained.rs @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + pub(crate) trait Constrained { type Unconstrained; } From 6e67a1c8f6608d5cbd7ff207c5d5aea69bf9e84b Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 25 Apr 2022 19:32:10 +0200 Subject: [PATCH 045/255] Fix AWS SDK --- .../software/amazon/smithy/rustsdk/AwsRuntimeDependency.kt | 5 +++-- .../software/amazon/smithy/rustsdk/InlineAwsDependency.kt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeDependency.kt index ff2f25875a..979a613822 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeDependency.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.rust.codegen.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeCrateLocation import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -43,12 +44,12 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { object AwsRuntimeType { val S3Errors by lazy { RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("s3_errors")) } val Presigning by lazy { - RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", public = true)) + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) } fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( - "middleware", public = true, + "middleware", visibility = Visibility.PUBLIC, CargoDependency.SmithyHttp(this), CargoDependency.SmithyHttpTower(this), CargoDependency.SmithyClient(this), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt index 2f0a1b1e2f..9f329f765b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt @@ -7,8 +7,9 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.rust.codegen.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.rustlang.RustDependency +import software.amazon.smithy.rust.codegen.rustlang.Visibility object InlineAwsDependency { - fun forRustFile(file: String, public: Boolean = false, vararg additionalDependency: RustDependency): InlineDependency = - InlineDependency.Companion.forRustFile(file, "aws-inlineable", public, *additionalDependency) + fun forRustFile(file: String, visibility: Visibility = Visibility.PRIVATE, vararg additionalDependency: RustDependency): InlineDependency = + InlineDependency.Companion.forRustFile(file, "aws-inlineable", visibility, *additionalDependency) } From a274ec5375f9ee9edecc4d2b1d56fb8e4f46f118 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 28 Apr 2022 18:40:24 +0200 Subject: [PATCH 046/255] Don't crash codegen with unsupported constraint traits --- .../server/smithy/ConstraintViolationSymbolProvider.kt | 9 ++++++--- .../codegen/smithy/UnconstrainedShapeSymbolProvider.kt | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 10e1ba6f10..4b83e24043 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -50,13 +50,15 @@ class ConstraintViolationSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { is SetShape -> { - TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") +// TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") + base.toSymbol(shape) } is ListShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on list shapes is currently not implemented") +// TODO("The `length` constraint trait on list shapes is currently not implemented") + base.toSymbol(shape) } else { unconstrainedSymbolForListOrMapShape(shape) } @@ -65,7 +67,8 @@ class ConstraintViolationSymbolProvider( check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on map shapes is currently not implemented") +// TODO("The `length` constraint trait on map shapes is currently not implemented") + base.toSymbol(shape) } else { unconstrainedSymbolForListOrMapShape(shape) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index ffcffbef8a..456de80941 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -47,13 +47,15 @@ class UnconstrainedShapeSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { is SetShape -> { - TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") +// TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") + base.toSymbol(shape) } is ListShape -> { check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on list shapes is currently not implemented") +// TODO("The `length` constraint trait on list shapes is currently not implemented") + base.toSymbol(shape) } else { unconstrainedSymbolForCollectionOrMapShape(shape) } @@ -62,7 +64,8 @@ class UnconstrainedShapeSymbolProvider( check(shape.canReachConstrainedShape(model, base)) if (shape.isConstrained(base)) { - TODO("The `length` constraint trait on map shapes is currently not implemented") +// TODO("The `length` constraint trait on map shapes is currently not implemented") + base.toSymbol(shape) } else { unconstrainedSymbolForCollectionOrMapShape(shape) } From 3a9bc5622f4d1764a7ce9c9698c2cf4a40bae9cd Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 5 May 2022 16:03:45 +0200 Subject: [PATCH 047/255] make svt yield fewer errors --- .../server/smithy/ConstraintViolationSymbolProvider.kt | 4 ++-- .../rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 4b83e24043..07c20d85ef 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -58,7 +58,7 @@ class ConstraintViolationSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on list shapes is currently not implemented") - base.toSymbol(shape) + unconstrainedSymbolForListOrMapShape(shape) } else { unconstrainedSymbolForListOrMapShape(shape) } @@ -68,7 +68,7 @@ class ConstraintViolationSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on map shapes is currently not implemented") - base.toSymbol(shape) + unconstrainedSymbolForListOrMapShape(shape) } else { unconstrainedSymbolForListOrMapShape(shape) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 456de80941..84c32fb9de 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -55,7 +55,7 @@ class UnconstrainedShapeSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on list shapes is currently not implemented") - base.toSymbol(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } else { unconstrainedSymbolForCollectionOrMapShape(shape) } @@ -65,7 +65,7 @@ class UnconstrainedShapeSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on map shapes is currently not implemented") - base.toSymbol(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } else { unconstrainedSymbolForCollectionOrMapShape(shape) } From 2fe280d3c0e4bf5a9ca858e94fd47f80c11b9373 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 5 May 2022 16:05:00 +0200 Subject: [PATCH 048/255] disable svt --- codegen-server-test/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index f0f156dfcf..72aa2cb2be 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -35,7 +35,8 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), - CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), + // TODO Disabling until I fix `Constrained` trait possibly being implemented for the same type twice. +// CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), CodegenTest("aws.protocoltests.misc#MiscService", "misc"), From 8864f1d2e62a175c3aa6b802b3b5552c15aca7b4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 5 May 2022 17:45:19 +0200 Subject: [PATCH 049/255] operation_deser builders take in unconstrained types --- .../server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 12307426ec..f1e4e2dc6c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -626,7 +626,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( serverRenderUriPathParser(this, operationShape) serverRenderQueryStringParser(this, operationShape) - val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider, takeInUnconstrainedTypes = false)) { + val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider, takeInUnconstrainedTypes = true)) { "?" } else "" rustTemplate("input.build()$err", *codegenScope) From b77f6fb3e39c524f56c087fa27c4d12eeff74349 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 5 May 2022 18:19:27 +0200 Subject: [PATCH 050/255] fix aws sdk sso --- .../protocols/parse/JsonParserGenerator.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 2d40db8ad3..4a62bbfde2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -212,20 +212,26 @@ class JsonParserGenerator( rustBlock("match key.to_unescaped()?.as_ref()") { for (member in members) { rustBlock("${jsonName(member).dq()} =>") { - if (symbolProvider.toSymbol(member).isOptional()) { + if (mode == CodegenMode.Client) { withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(", ");") { deserializeMember(member) } } else { - rust("if let Some(v) = ") - deserializeMember(member) - rust( - """ - { - builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(v); + if (symbolProvider.toSymbol(member).isOptional()) { + withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(", ");") { + deserializeMember(member) } - """ - ) + } else { + rust("if let Some(v) = ") + deserializeMember(member) + rust( + """ + { + builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, mode)}(v); + } + """ + ) + } } } } From 07da98f36a42f03770d4cf848c93951f704f3705 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 6 May 2022 15:27:48 +0200 Subject: [PATCH 051/255] Revert "disable svt" This reverts commit 2fe280d3c0e4bf5a9ca858e94fd47f80c11b9373. --- codegen-server-test/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 72aa2cb2be..f0f156dfcf 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -35,8 +35,7 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), - // TODO Disabling until I fix `Constrained` trait possibly being implemented for the same type twice. -// CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), + CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), CodegenTest("aws.protocoltests.misc#MiscService", "misc"), From 0cc2644c3832d522f0509fd24d989222de92175f Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 9 May 2022 13:42:38 +0200 Subject: [PATCH 052/255] changes in UnconstrainedShapeSymbolProvider --- .../rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 84c32fb9de..2c86661363 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape @@ -21,7 +20,7 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase fun unconstrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceShape): String { - check(shape is CollectionShape || shape.isMapShape) + check(shape.isListShape || shape.isMapShape) return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" } From e9e8205865a710120c5a2f0b3e2d3954ccb99980 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 9 May 2022 19:24:05 +0200 Subject: [PATCH 053/255] sst works for lists --- codegen-server-test/model/simple.smithy | 67 ++++++++------- .../ConstraintViolationSymbolProvider.kt | 1 + .../server/smithy/ServerCodegenVisitor.kt | 22 ++++- .../generators/ConstrainedListGenerator.kt | 47 ++++++++++ .../generators/ServerBuilderGenerator.kt | 67 ++++++++++++--- .../generators/UnconstrainedListGenerator.kt | 20 ++--- .../smithy/ConstrainedShapeSymbolProvider.kt | 86 +++++++++++++++++++ .../rust/codegen/smithy/SymbolVisitor.kt | 1 + .../UnconstrainedShapeSymbolProvider.kt | 10 +-- 9 files changed, 261 insertions(+), 60 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 07953c8ec2..2b590c5a74 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -26,22 +26,23 @@ structure AnOperationInput { } structure AnOperationOutput { - conA: ConA, + // conA: ConA, - conCList: ConCList, + // conCList: ConCList, } structure ConA { - @required - conB: ConB, + // @required + // conB: ConB, - optConB: ConB, + // optConB: ConB, conBList: ConBList, + conBList2: ConBList2, // conBSet: ConBSet, - conBMap: ConBMap + // conBMap: ConBMap } structure ConB { @@ -77,32 +78,40 @@ structure ConB { // foo: ValidList // } -list ConBList { - member: AnotherList -} - -list ConCList { - member: AnotherList -} +// list ConBList { +// member: AnotherList +// } -list AnotherList { +list ConBList { member: ConB } -set ConBSet { - member: AnotherSet -} - -set AnotherSet { - member: String -} - -map ConBMap { - key: String, - value: AnotherMap +list ConBList2 { + member: ConB } -map AnotherMap { - key: String, - value: ConBList -} +// list ConCList { +// member: AnotherList +// } +// +// list AnotherList { +// member: ConB +// } +// +// set ConBSet { +// member: AnotherSet +// } +// +// set AnotherSet { +// member: String +// } +// +// map ConBMap { +// key: String, +// value: AnotherMap +// } +// +// map AnotherMap { +// key: String, +// value: ConBList +// } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 07c20d85ef..05a43ad51b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -36,6 +36,7 @@ class ConstraintViolationSymbolProvider( private fun unconstrainedSymbolForListOrMapShape(shape: Shape): Symbol { check(shape is ListShape || shape is MapShape) + // TODO Move ConstraintViolation type to the constrained namespace. val unconstrainedTypeName = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index f9ca49c075..9d302f36e7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -27,6 +28,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings @@ -65,6 +67,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val symbolProvider: RustSymbolProvider private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider private val rustCrate: RustCrate private val fileManifest = context.fileManifest @@ -73,6 +76,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator private val unconstrainedModule = RustModule.private("unconstrained", "Unconstrained types for constrained shapes.") + private val constrainedModule = RustModule.private("constrained", "Constrained types for constrained shapes.") private val shapesReachableFromOperationInputs: Set init { @@ -98,6 +102,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, mode = CodegenMode.Server) @@ -187,7 +192,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: val builderGenerator = ServerBuilderGenerator( codegenContext, shape, - takeInUnconstrainedTypes = shapesReachableFromOperationInputs.contains(shape) + if (shapesReachableFromOperationInputs.contains(shape)) constrainedShapeSymbolProvider else null ) builderGenerator.render(writer) writer.implBlock(shape, symbolProvider) { @@ -203,17 +208,28 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: override fun listShape(shape: ListShape) { if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") - rustCrate.withModule(unconstrainedModule) { writer -> UnconstrainedListGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape ).render() } + + logger.info("[rust-server-codegen] Generating a constrained type for list $shape") + rustCrate.withModule(constrainedModule) { writer -> + ConstrainedListGenerator( + model, + symbolProvider, + constrainedShapeSymbolProvider, + writer, + shape + ).render() + } } } @@ -231,6 +247,8 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: shape ).render() } + + // TODO ConstrainedMapGenerator } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt new file mode 100644 index 0000000000..82d497e0e8 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape + +// TODO Docs +// TODO Unit tests +class ConstrainedListGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + val writer: RustWriter, + val shape: ListShape +) { + fun render() { + check(shape.canReachConstrainedShape(model, symbolProvider)) + + val symbol = constrainedShapeSymbolProvider.toSymbol(shape) + val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name + val innerShape = model.expectShape(shape.member.target) + val innerSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) + + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) Vec<#{InnerConstrainedSymbol}>); + + """, + "InnerConstrainedSymbol" to innerSymbol, + ) + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 81eda63ee1..f243f5521d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -24,6 +24,7 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata @@ -48,11 +49,13 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. // - This builder is not `PartialEq`. // - Always implements either From for Structure or TryFrom for Structure. +// - `constrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. class ServerBuilderGenerator( private val codegenContext: CodegenContext, private val shape: StructureShape, - private val takeInUnconstrainedTypes: Boolean = false, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider? = null, ) { + private val takeInUnconstrainedTypes = constrainedShapeSymbolProvider != null private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val constraintViolationSymbolProvider = @@ -223,19 +226,39 @@ class ServerBuilderGenerator( rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { - val constrainedType = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + val constrainedType = constrainedShapeSymbolProvider!!.toSymbol(member) + val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape + // TODO Refactor this. 3 conditions (isOptional, hasBox, constrainedTypeHoldsFinalType) if (symbol.isOptional()) { + val innerConstrainedType = constrainedType.mapRustType { it.stripOuter() } if (hasBox) { - write("input.map(|v| Box::new($constrainedType(*v)))") + if (constrainedTypeHoldsFinalType) { + rust("input.map(|v| Box::new($maybeConstrainedConstrained(*v)))") + } else { + rust("input.map(|v| Box::new($maybeConstrainedConstrained(#T(*v))))", innerConstrainedType) + } } else { - write("input.map(|v| $constrainedType(v))") + if (constrainedTypeHoldsFinalType) { + rust("input.map(|v| $maybeConstrainedConstrained(v))") + } else { + rust("input.map(|v| $maybeConstrainedConstrained(#T(v)))", innerConstrainedType) + } } } else { if (hasBox) { // TODO Add a protocol test testing this branch. - write("Box::new($constrainedType(*input))") + if (constrainedTypeHoldsFinalType) { + rust("Box::new($maybeConstrainedConstrained(*input))") + } else { + rust("Box::new($maybeConstrainedConstrained(#T(*input)))", constrainedType) + } } else { - write("$constrainedType(input)") + if (constrainedTypeHoldsFinalType) { + rust("$maybeConstrainedConstrained(input)") + } else { + rust("$maybeConstrainedConstrained(#T(input))", constrainedType) + } } } } else { @@ -248,7 +271,9 @@ class ServerBuilderGenerator( } /** - * Render a `set_foo` method. This method is able to take in builders of structure shape types. + * Render a `set_foo` method. + * This method is able to take in unconstrained types for constrained shapes, like builders of structs in the case + * of structure shapes. * * This method is only used by deserializers at the moment and is therefore `pub(crate)`. */ @@ -402,7 +427,7 @@ class ServerBuilderGenerator( */ private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes) { - val strippedOption = symbolProvider.toSymbol(member) + val strippedOption = constrainedShapeSymbolProvider!!.toSymbol(member) // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } @@ -410,7 +435,8 @@ class ServerBuilderGenerator( strippedOption // Strip the `Box` in case the member can reach itself recursively. .mapRustType { it.stripOuter() } - // Wrap it in the Cow-like `constrained::Constrained` type in case the target member shape can reach a constrained shape. + // Wrap it in the Cow-like `constrained::MaybeConstrained` type in case the target member shape can + // reach a constrained shape. .letIf(member.targetCanReachConstrainedShape(model, symbolProvider)) { it.wrapMaybeConstrained() } // Box it in case the member can reach itself recursively. .letIf(hadBox) { it.makeRustBoxed() } @@ -424,19 +450,26 @@ class ServerBuilderGenerator( * Writes the code to instantiate the struct the builder builds. * * Builder member types are either: - * 1. `Option>`; or - * 2. `Option`. + * 1. `Option>`; or + * 2. `Option`. + * + * Where `U` is a constrained type. * * The structs they build have member types: * a) `Option`; or * b) `T`. * - * For each member, this function first safely unwraps case 1. into 2., and then converts into b) if necessary. + * `U` is equal to `T` when the shape for `U` has a constraint trait or the member shape is a structure shape. + * Otherwise, `U` is always a `pub(crate)` tuple newtype holding `T`. + * + * For each member, this function first safely unwraps case 1. into 2., then converts `U` into `T` if necessary, + * and then converts into b) if necessary. */ private fun coreBuilder(writer: RustWriter) { writer.rustBlock("#T", structureSymbol) { for (member in members) { val memberName = symbolProvider.toMemberName(member) + val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). @@ -455,7 +488,10 @@ class ServerBuilderGenerator( Ok(Box::new(x.try_into()?)) } }) - .map(|v| v.map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err)))) + .map(|res| + res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.0)" } + .map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err))) + ) .transpose()? """, "MaybeConstrained" to RuntimeType.MaybeConstrained() @@ -470,7 +506,10 @@ class ServerBuilderGenerator( x.try_into() } }) - .map(|v| v.map_err(|err| ConstraintViolation::${constraintViolation.name()}(err))) + .map(|res| + res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.0)" } + .map_err(|err| ConstraintViolation::${constraintViolation.name()}(err)) + ) .transpose()? """, "MaybeConstrained" to RuntimeType.MaybeConstrained() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index f149a67584..226289605e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -23,6 +24,7 @@ class UnconstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: ListShape @@ -34,16 +36,13 @@ class UnconstrainedListGenerator( val module = symbol.namespace.split(symbol.namespaceDelimiter).last() val name = symbol.name val innerShape = model.expectShape(shape.member.target) - val innerSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199): We will need a `ConstrainedSymbolProvider` when we have constraint traits. - val constrainedSymbol = symbolProvider.toSymbol(shape) + val innerUnconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) + val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - // TODO The implementation of the `Constrained` trait is probably not for the correct type. There might be more than - // one "path" to an e.g. Vec> with different constraint traits along the path, because constraint - // traits can be applied to members, or simply because the model might have two different lists holding `StructA`. - // So we will have to newtype things. + // TODO Move implementation of ConstrainedTrait to the constrained module. + // TODO Don't be lazy and don't use `Result<_, >`. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -67,7 +66,7 @@ class UnconstrainedListGenerator( type Error = $constraintViolationName; fn try_from(value: $name) -> Result { - let res: Result = value + let res: Result<_, #{InnerConstraintViolationSymbol}> = value .0 .into_iter() .map(|inner| { @@ -75,11 +74,12 @@ class UnconstrainedListGenerator( inner.try_into() }) .collect(); - res.map_err(|err| ConstraintViolation(err)) + res.map(|inner| Self(inner)) + .map_err(|err| ConstraintViolation(err)) } } """, - "InnerUnconstrainedSymbol" to innerSymbol, + "InnerUnconstrainedSymbol" to innerUnconstrainedSymbol, "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, "ConstrainedSymbol" to constrainedSymbol, "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt new file mode 100644 index 0000000000..81812ce98d --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt @@ -0,0 +1,86 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.smithy + +import software.amazon.smithy.codegen.core.Symbol +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.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.util.toPascalCase +import software.amazon.smithy.rust.codegen.util.toSnakeCase + +fun constrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceShape): String { + check(shape.isListShape || shape.isMapShape) + return "${shape.id.getName(serviceShape).toPascalCase()}Constrained" +} + +class ConstrainedShapeSymbolProvider( + private val base: RustSymbolProvider, + private val model: Model, + private val serviceShape: ServiceShape, +) : WrappingSymbolProvider(base) { + private fun constrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { + check(shape is ListShape || shape is MapShape) + + val name = constrainedTypeNameForListOrMapShape(shape, serviceShape) + val namespace = "crate::${Constrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" + val rustType = RustType.Opaque(name, namespace) + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Constrained.filename) + .build() + } + + override fun toSymbol(shape: Shape): Symbol = + when (shape) { + is ListShape -> { + check(shape.canReachConstrainedShape(model, base)) + + if (shape.isConstrained(base)) { +// TODO("The `length` constraint trait on list shapes is currently not implemented") + constrainedSymbolForCollectionOrMapShape(shape) + } else { + constrainedSymbolForCollectionOrMapShape(shape) + } + } + is MapShape -> { + check(shape.canReachConstrainedShape(model, base)) + + if (shape.isConstrained(base)) { +// TODO("The `length` constraint trait on map shapes is currently not implemented") + constrainedSymbolForCollectionOrMapShape(shape) + } else { + constrainedSymbolForCollectionOrMapShape(shape) + } + } + is MemberShape -> { + // TODO This only applies really if the member shape is a structure member. We could check in + // the beginning of this case that our containing shape is a structure shape maybe? + if (shape.isConstrained(base)) { + // TODO Constraint traits precedence has a role here. +// base.toSymbol(shape) + val targetShape = model.expectShape(shape.target) + this.toSymbol(targetShape) + } else { + val targetShape = model.expectShape(shape.target) + this.toSymbol(targetShape) + // The member shape is not constrained, so it doesn't have `required`. + .makeOptional() + } + } + else -> { + // TODO Other shape types. + base.toSymbol(shape) + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 6572696270..4e46fffce6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -91,6 +91,7 @@ val Serializers = SymbolLocation("serializer") val Inputs = SymbolLocation("input") val Outputs = SymbolLocation("output") val Unconstrained = SymbolLocation("unconstrained") +val Constrained = SymbolLocation("constrained") /** * Make the Rust type of a symbol optional (hold `Option`) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 2c86661363..9ca7f915be 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -29,7 +29,7 @@ class UnconstrainedShapeSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { - private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { + private fun unconstrainedSymbolForListOrMapShape(shape: Shape): Symbol { check(shape is ListShape || shape is MapShape) val name = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) @@ -54,9 +54,9 @@ class UnconstrainedShapeSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on list shapes is currently not implemented") - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } else { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } } is MapShape -> { @@ -64,9 +64,9 @@ class UnconstrainedShapeSymbolProvider( if (shape.isConstrained(base)) { // TODO("The `length` constraint trait on map shapes is currently not implemented") - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } else { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForListOrMapShape(shape) } } is StructureShape -> { From 4422b5252db1118dc80c90b5d843e9e7ae72c6b9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 9 May 2022 22:58:47 +0200 Subject: [PATCH 054/255] sst and srt work for lists --- .../generators/ServerBuilderGenerator.kt | 6 +- .../smithy/ConstrainedShapeSymbolProvider.kt | 56 +++++++++++++++---- .../smithy/rust/codegen/smithy/Constraints.kt | 13 ++++- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index f243f5521d..18a760c4f8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -426,7 +426,7 @@ class ServerBuilderGenerator( * All builder members are optional, but only some are `Option`s where `T` needs to be constrained. */ private fun builderMemberSymbol(member: MemberShape): Symbol = - if (takeInUnconstrainedTypes) { + if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val strippedOption = constrainedShapeSymbolProvider!!.toSymbol(member) // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } @@ -435,9 +435,9 @@ class ServerBuilderGenerator( strippedOption // Strip the `Box` in case the member can reach itself recursively. .mapRustType { it.stripOuter() } - // Wrap it in the Cow-like `constrained::MaybeConstrained` type in case the target member shape can + // Wrap it in the Cow-like `constrained::MaybeConstrained` type, since we know the target member shape can // reach a constrained shape. - .letIf(member.targetCanReachConstrainedShape(model, symbolProvider)) { it.wrapMaybeConstrained() } + .wrapMaybeConstrained() // Box it in case the member can reach itself recursively. .letIf(hadBox) { it.makeRustBoxed() } // Ensure we always end up with an `Option`. diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt index 81812ce98d..7f8560b2e5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt @@ -7,13 +7,17 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex 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.ServiceShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.SimpleShape +import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -27,6 +31,8 @@ class ConstrainedShapeSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { + private val nullableIndex = NullableIndex.of(model) + private fun constrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { check(shape is ListShape || shape is MapShape) @@ -41,6 +47,27 @@ class ConstrainedShapeSymbolProvider( .build() } + // TODO The following two methods have been copied from `SymbolVisitor.kt`. + private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = + if (member.isRequired) { + symbol + } else if (nullableIndex.isNullable(member)) { + symbol.makeOptional() + } else { + symbol + } + + /** + * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with + * [RustBoxTrait]; otherwise returns [symbol] unchanged. + * + * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. + */ + private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = + if (shape.hasTrait()) { + symbol.makeRustBoxed() + } else symbol + override fun toSymbol(shape: Shape): Symbol = when (shape) { is ListShape -> { @@ -64,18 +91,27 @@ class ConstrainedShapeSymbolProvider( } } is MemberShape -> { - // TODO This only applies really if the member shape is a structure member. We could check in - // the beginning of this case that our containing shape is a structure shape maybe? - if (shape.isConstrained(base)) { - // TODO Constraint traits precedence has a role here. -// base.toSymbol(shape) - val targetShape = model.expectShape(shape.target) - this.toSymbol(targetShape) + check(shape.canReachConstrainedShape(model, base)) { + shape.id + } + check(model.expectShape(shape.container).isStructureShape) + + if (shape.requiresNewtype()) { + TODO() } else { val targetShape = model.expectShape(shape.target) - this.toSymbol(targetShape) - // The member shape is not constrained, so it doesn't have `required`. - .makeOptional() + + if (targetShape is SimpleShape) { + check(shape.hasTrait()) { + "Targeting a simple shape that can reach a constrained shape and does not need a newtype; the member shape must be `required`" + } + + base.toSymbol(shape) + } else { + val targetSymbol = this.toSymbol(targetShape) + // Handle boxing first so we end up with `Option>`, not `Box>`. + handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + } } } else -> { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index faf994f614..ff9deb3cc2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -71,7 +71,18 @@ fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvid return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider, visited) } -// TODO Perhaps move these into `StructureGenerator`? +fun MemberShape.requiresNewtype() = + // Note that member shapes whose only constraint trait is `required` do not require a newtype. + this.hasTrait() || + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() + +fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + this.isConstrained(symbolProvider) || this.targetCanReachConstrainedShape(model, symbolProvider) + +// TODO Callers should use `MemberShape.canReachConstrainedShape`, and this function should be inlined. fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = when (val targetShape = model.expectShape(this.target)) { is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) From 8c46710f3d74ac9d68314820f7c4ec7fb181618a Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 11:24:35 +0200 Subject: [PATCH 055/255] svt not working for maps --- .../server/smithy/ServerCodegenVisitor.kt | 13 +++- .../generators/ConstrainedMapGenerator.kt | 73 +++++++++++++++++++ .../generators/UnconstrainedMapGenerator.kt | 19 +++-- .../smithy/ConstrainedShapeSymbolProvider.kt | 14 +++- 4 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 9d302f36e7..d32240afbb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -242,13 +243,23 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: model, symbolProvider, unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape ).render() } - // TODO ConstrainedMapGenerator + logger.info("[rust-server-codegen] Generating a constrained type for map $shape") + rustCrate.withModule(constrainedModule) { writer -> + ConstrainedMapGenerator( + model, + symbolProvider, + constrainedShapeSymbolProvider, + writer, + shape + ).render() + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt new file mode 100644 index 0000000000..a76c00da69 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape +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.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait + +// TODO Docs +// TODO Unit tests +class ConstrainedMapGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + val writer: RustWriter, + val shape: MapShape +) { + fun render() { + check(shape.canReachConstrainedShape(model, symbolProvider)) + + val symbol = constrainedShapeSymbolProvider.toSymbol(shape) + val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name + val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + val valueShape = model.expectShape(shape.value.target) + val keySymbol = if (isKeyConstrained(keyShape)) { + constrainedShapeSymbolProvider.toSymbol(keyShape) + } else { + symbolProvider.toSymbol(keyShape) + } + val valueSymbol = if (isValueConstrained(valueShape)) { + constrainedShapeSymbolProvider.toSymbol(valueShape) + } else { + symbolProvider.toSymbol(valueShape) + } + + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + """, + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + ) + } + } + + // TODO These are copied from `UnconstrainedMapGenerator.kt`. + private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() + + private fun isValueConstrained(shape: Shape): Boolean = when (shape) { + is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) + is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) + is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. + else -> false + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index a565c7d339..7901e6c928 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -16,11 +16,11 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained // TODO Docs @@ -28,6 +28,7 @@ class UnconstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: MapShape @@ -50,8 +51,7 @@ class UnconstrainedMapGenerator( } else { symbolProvider.toSymbol(valueShape) } - // TODO(https://github.com/awslabs/smithy-rs/pull/1199): We will need a `ConstrainedSymbolProvider` when we have constraint traits. - val constrainedSymbol = symbolProvider.toSymbol(shape) + val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val constraintViolationCodegenScope = listOfNotNull( if (isKeyConstrained(keyShape)) { @@ -66,10 +66,6 @@ class UnconstrainedMapGenerator( }, ).toTypedArray() - // TODO The implementation of the `Constrained` trait is probably not for the correct type. There might be more than - // one "path" to an e.g. HashMap> with different constraint traits along the path, because constraint - // traits can be applied to members, or simply because the model might have two different maps holding `StructA`. - // So we will have to newtype things. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -96,7 +92,7 @@ class UnconstrainedMapGenerator( type Error = $constraintViolationName; fn try_from(value: $name) -> Result { - value + let res: Result<_, Self::Error> = value .0 .into_iter() .map(|(k, v)| { @@ -105,7 +101,8 @@ class UnconstrainedMapGenerator( ${ if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(|err| Self::Error::Value(err))?;" else "" } Ok((k, v)) }) - .collect() + .collect(); + res.map(|inner| Self(inner)) } } """, @@ -119,7 +116,9 @@ class UnconstrainedMapGenerator( } } - private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() + // TODO This is the correct implementation when we have constraint traits. +// private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() + private fun isKeyConstrained(shape: StringShape) = false private fun isValueConstrained(shape: Shape): Boolean = when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt index 7f8560b2e5..91e02f3873 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt @@ -97,7 +97,19 @@ class ConstrainedShapeSymbolProvider( check(model.expectShape(shape.container).isStructureShape) if (shape.requiresNewtype()) { - TODO() + //TODO() + + // TODO What follows is wrong; here we should refer to an opaque type for the member shape. + // But for now we add this to not make the validation model crash. + + val targetShape = model.expectShape(shape.target) + if (targetShape is SimpleShape) { + base.toSymbol(shape) + } else { + val targetSymbol = this.toSymbol(targetShape) + // Handle boxing first so we end up with `Option>`, not `Box>`. + handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + } } else { val targetShape = model.expectShape(shape.target) From 831bfc0e44d884f592d73d1cdb3b70ac2236f79b Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 13:17:41 +0200 Subject: [PATCH 056/255] sst list of list works --- codegen-server-test/model/simple.smithy | 20 +++++++--------- .../generators/ConstrainedListGenerator.kt | 24 +++++++++++++++---- .../generators/ServerBuilderGenerator.kt | 14 +++++------ 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 2b590c5a74..626b773bd8 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -38,7 +38,7 @@ structure ConA { // optConB: ConB, conBList: ConBList, - conBList2: ConBList2, + // conBList2: ConBList2, // conBSet: ConBSet, @@ -78,25 +78,21 @@ structure ConB { // foo: ValidList // } -// list ConBList { -// member: AnotherList -// } - list ConBList { - member: ConB + member: AnotherList } -list ConBList2 { - member: ConB -} +// list ConBList2 { +// member: ConB +// } // list ConCList { // member: AnotherList // } // -// list AnotherList { -// member: ConB -// } +list AnotherList { + member: ConB +} // // set ConBSet { // member: AnotherSet diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index 82d497e0e8..61ae7d2145 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -27,11 +27,13 @@ class ConstrainedListGenerator( fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val symbol = constrainedShapeSymbolProvider.toSymbol(shape) - val module = symbol.namespace.split(symbol.namespaceDelimiter).last() - val name = symbol.name + val symbol = symbolProvider.toSymbol(shape) + val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + // TODO Perhaps it's overkill to create this module when the type is pub. + val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() + val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) - val innerSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) + val innerConstrainedSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( @@ -39,8 +41,20 @@ class ConstrainedListGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) Vec<#{InnerConstrainedSymbol}>); + impl From<#{Symbol}> for $name { + fn from(v: #{Symbol}) -> Self { + Self(v.into_iter().map(|item| item.into()).collect()) + } + } + + impl From<$name> for #{Symbol} { + fn from(v: $name) -> Self { + v.0.into_iter().map(|item| item.into()).collect() + } + } """, - "InnerConstrainedSymbol" to innerSymbol, + "InnerConstrainedSymbol" to innerConstrainedSymbol, + "Symbol" to symbol, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 18a760c4f8..259a750760 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -227,22 +227,20 @@ class ServerBuilderGenerator( conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - val constrainedType = constrainedShapeSymbolProvider!!.toSymbol(member) val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape // TODO Refactor this. 3 conditions (isOptional, hasBox, constrainedTypeHoldsFinalType) if (symbol.isOptional()) { - val innerConstrainedType = constrainedType.mapRustType { it.stripOuter() } if (hasBox) { if (constrainedTypeHoldsFinalType) { rust("input.map(|v| Box::new($maybeConstrainedConstrained(*v)))") } else { - rust("input.map(|v| Box::new($maybeConstrainedConstrained(#T(*v))))", innerConstrainedType) + rust("input.map(|v| Box::new($maybeConstrainedConstrained((*v).into())))") } } else { if (constrainedTypeHoldsFinalType) { rust("input.map(|v| $maybeConstrainedConstrained(v))") } else { - rust("input.map(|v| $maybeConstrainedConstrained(#T(v)))", innerConstrainedType) + rust("input.map(|v| $maybeConstrainedConstrained(v.into()))") } } } else { @@ -251,13 +249,13 @@ class ServerBuilderGenerator( if (constrainedTypeHoldsFinalType) { rust("Box::new($maybeConstrainedConstrained(*input))") } else { - rust("Box::new($maybeConstrainedConstrained(#T(*input)))", constrainedType) + rust("Box::new($maybeConstrainedConstrained((*input).into()))") } } else { if (constrainedTypeHoldsFinalType) { rust("$maybeConstrainedConstrained(input)") } else { - rust("$maybeConstrainedConstrained(#T(input))", constrainedType) + rust("$maybeConstrainedConstrained(input.into())") } } } @@ -489,7 +487,7 @@ class ServerBuilderGenerator( } }) .map(|res| - res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.0)" } + res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } .map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err))) ) .transpose()? @@ -507,7 +505,7 @@ class ServerBuilderGenerator( } }) .map(|res| - res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.0)" } + res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } .map_err(|err| ConstraintViolation::${constraintViolation.name()}(err)) ) .transpose()? From 465139bb7a16ba55540e997cdb9ba9a53aa871c0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 13:43:17 +0200 Subject: [PATCH 057/255] sst map of map works --- codegen-server-test/model/simple.smithy | 21 +++++++++-------- .../generators/ConstrainedListGenerator.kt | 4 +++- .../generators/ConstrainedMapGenerator.kt | 23 ++++++++++++++++--- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 626b773bd8..a9bbf9ddf9 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -37,12 +37,12 @@ structure ConA { // optConB: ConB, - conBList: ConBList, + // conBList: ConBList, // conBList2: ConBList2, // conBSet: ConBSet, - // conBMap: ConBMap + conBMap: ConBMap } structure ConB { @@ -102,12 +102,13 @@ list AnotherList { // member: String // } // -// map ConBMap { -// key: String, -// value: AnotherMap -// } +map ConBMap { + key: String, + value: AnotherMap +} // -// map AnotherMap { -// key: String, -// value: ConBList -// } +map AnotherMap { + key: String, + value: ConBList + //value: ConB +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index 61ae7d2145..d59cf0b620 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -29,12 +29,14 @@ class ConstrainedListGenerator( val symbol = symbolProvider.toSymbol(shape) val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) - // TODO Perhaps it's overkill to create this module when the type is pub. val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) val innerConstrainedSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) + // The converters are only needed when the constrained type is `pub(crate)`, for the server builder function + // member function to work. + // Note that unless the list holds an aggregate shape, the `.into()` calls are useless. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index a76c00da69..68d2fb5489 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -32,9 +32,10 @@ class ConstrainedMapGenerator( fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val symbol = constrainedShapeSymbolProvider.toSymbol(shape) - val module = symbol.namespace.split(symbol.namespaceDelimiter).last() - val name = symbol.name + val symbol = symbolProvider.toSymbol(shape) + val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() + val name = constrainedSymbol.name val keyShape = model.expectShape(shape.key.target, StringShape::class.java) val valueShape = model.expectShape(shape.value.target) val keySymbol = if (isKeyConstrained(keyShape)) { @@ -48,14 +49,30 @@ class ConstrainedMapGenerator( symbolProvider.toSymbol(valueShape) } + // The converters are only needed when the constrained type is `pub(crate)`, for the server builder function + // member function to work. + // Note that unless the map holds an aggregate shape as its value shape, the `.into()` calls are useless. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + + impl From<#{Symbol}> for $name { + fn from(hm: #{Symbol}) -> Self { + Self(hm.into_iter().map(|(k, v)| (k, v.into())).collect()) + } + } + + impl From<$name> for #{Symbol} { + fn from(wrapper: $name) -> Self { + wrapper.0.into_iter().map(|(k, v)| (k, v.into())).collect() + } + } """, "KeySymbol" to keySymbol, "ValueSymbol" to valueSymbol, + "Symbol" to symbol, ) } } From ff0c43bcb5442312616e695e68566504ca65a282 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 14:07:48 +0200 Subject: [PATCH 058/255] this commit build sst, srt, and svt --- .../software/amazon/smithy/rust/codegen/smithy/Constraints.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index ff9deb3cc2..fbda3d00db 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -33,7 +33,8 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } else -> { - this.hasConstraintTrait() +// this.hasConstraintTrait() + false } } From 03ff5fd74fee8e2cfd6695aa5dfbbf1990583712 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 16:00:16 +0200 Subject: [PATCH 059/255] fix unit tests --- .../UnconstrainedListGeneratorTest.kt | 24 +++++++++++++++---- .../UnconstrainedMapGeneratorTest.kt | 18 ++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt index 753004b36a..1fffd30984 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -69,6 +70,18 @@ class UnconstrainedListGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } + val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + project.withModule(RustModule.private("constrained")) { writer -> + listOf(listA, listB).forEach { + ConstrainedListGenerator( + model, + symbolProvider, + constrainedShapeSymbolProvider, + writer, + it + ).render() + } + } project.withModule(RustModule.private("unconstrained")) { writer -> val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) @@ -77,6 +90,7 @@ class UnconstrainedListGeneratorTest { model, symbolProvider, unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, it @@ -99,7 +113,7 @@ class UnconstrainedListGeneratorTest { use std::convert::TryFrom; assert_eq!( expected_err, - Vec::>::try_from(list_a_unconstrained).unwrap_err() + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); """ ) @@ -111,15 +125,17 @@ class UnconstrainedListGeneratorTest { let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - let expected = vec![vec![crate::model::StructureC { + let expected: Vec> = vec![vec![crate::model::StructureC { string: String::from("david"), int: 69 }]]; + let actual: Vec> = + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); use std::convert::TryFrom; assert_eq!( expected, - Vec::>::try_from(list_a_unconstrained).unwrap() + actual ); """ ) @@ -131,7 +147,7 @@ class UnconstrainedListGeneratorTest { let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - let _list_a: crate::constrained::MaybeConstrained>> = list_a_unconstrained.into(); + let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); """ ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 45a3b93302..5c0b57ccd2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -71,6 +72,18 @@ class UnconstrainedMapGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } + val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + project.withModule(RustModule.private("constrained")) { writer -> + listOf(mapA, mapB).forEach { + ConstrainedMapGenerator( + model, + symbolProvider, + constrainedShapeSymbolProvider, + writer, + it + ).render() + } + } project.withModule(RustModule.private("unconstrained")) { writer -> val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) @@ -79,6 +92,7 @@ class UnconstrainedMapGeneratorTest { model, symbolProvider, unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, it @@ -143,7 +157,7 @@ class UnconstrainedMapGeneratorTest { use std::convert::TryFrom; assert_eq!( expected, - std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap() + crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); """ ) @@ -163,7 +177,7 @@ class UnconstrainedMapGeneratorTest { ]) ); - let _map_a: crate::constrained::MaybeConstrained>> = map_a_unconstrained.into(); + let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); """ ) From 8f322cd2b4714c12e55aa94404e0d22ca3eb626a Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 17:02:52 +0200 Subject: [PATCH 060/255] ConstraintTests.kt --- .../codegen/server/smithy/ConstraintsTest.kt | 159 ++++++++++++++++++ .../smithy/rust/codegen/smithy/Constraints.kt | 17 +- 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt new file mode 100644 index 0000000000..450b2e90b9 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -0,0 +1,159 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +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.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.requiresNewtype +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.util.lookup + +class ConstraintsTest { + private val model = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + map: MapA, + + recursive: RecursiveShape + } + + structure RecursiveShape { + shape: RecursiveShape, + mapB: MapB + } + + @length(min: 1, max: 69) + map MapA { + key: String, + value: MapB + } + + map MapB { + key: String, + value: StructureA + } + + @uniqueItems + list ListA { + member: MyString + } + + @pattern("\\w+") + string MyString + + structure StructureA { + @range(min: 1, max: 69) + int: Integer, + + @required + string: String + } + + // This shape is not in the service closure. + structure StructureB { + @pattern("\\w+") + patternString: String, + + @required + requiredString: String, + + mapA: MapA, + + @length(min: 1, max: 5) + mapAPrecedence: MapA + } + """.asSmithyModel() + private val symbolProvider = serverTestSymbolProvider(model) + + private val testInputOutput = model.lookup("test#TestInputOutput") + private val recursiveShape = model.lookup("test#RecursiveShape") + private val mapA = model.lookup("test#MapA") + private val mapB = model.lookup("test#MapB") + private val listA = model.lookup("test#ListA") + private val myString = model.lookup("test#MyString") + private val structA = model.lookup("test#StructureA") + private val structAInt = model.lookup("test#StructureA\$int") + private val structAString = model.lookup("test#StructureA\$string") + + @Test + fun `it should recognize constraint traits`() { + listOf(mapA, structAInt, structAString, myString).forEach { + it.hasConstraintTrait() shouldBe true + } + + listOf(mapB, structA).forEach { + it.hasConstraintTrait() shouldBe false + } + } + + @Test + fun `it should not recognize uniqueItems as a constraint trait because it's deprecated`() { + listA.hasConstraintTrait() shouldBe false + } + + @Test + fun `it should detect supported constrained traits as constrained`() { + structA.isConstrained(symbolProvider) shouldBe true + } + + @Test + fun `it should not detect unsupported constrained traits as constrained`() { + listOf(mapA, structAInt, structAString, myString).forEach { + it.isConstrained(symbolProvider) shouldBe false + } + } + + @Test + fun `it should evaluate reachability of constrained shapes`() { + mapA.canReachConstrainedShape(model, symbolProvider) shouldBe true + structAInt.canReachConstrainedShape(model, symbolProvider) shouldBe false + + // This should be true when we start supporting the `pattern` trait on string shapes. + listA.canReachConstrainedShape(model, symbolProvider) shouldBe false + + // All of these eventually reach `StructureA`, which is constrained because one of its members is `required`. + testInputOutput.canReachConstrainedShape(model, symbolProvider) shouldBe true + mapB.canReachConstrainedShape(model, symbolProvider) shouldBe true + recursiveShape.canReachConstrainedShape(model, symbolProvider) shouldBe true + } + + @Test + fun `only some constraint traits on member shapes should warrant a newtype`() { + structAInt.requiresNewtype() shouldBe true + structAString.requiresNewtype() shouldBe false + + val structBPatternString = model.lookup("test#StructureB\$patternString") + val structBRequiredString = model.lookup("test#StructureB\$requiredString") + val structBMapA = model.lookup("test#StructureB\$mapA") + val structBMapAPrecedence = model.lookup("test#StructureB\$mapAPrecedence") + + structBPatternString.requiresNewtype() shouldBe true + structBRequiredString.requiresNewtype() shouldBe false + structBMapA.requiresNewtype() shouldBe false + structBMapAPrecedence.requiresNewtype() shouldBe true + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index fbda3d00db..f93864812d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -17,6 +17,9 @@ import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Unit test these functions and then refactor to use a `Walker` instead of hand-rolling our own DFS. +/** + * A shape has a constraint trait if it has one of these traits attached. + */ fun Shape.hasConstraintTrait() = this.hasTrait() || this.hasTrait() || @@ -25,6 +28,17 @@ fun Shape.hasConstraintTrait() = // this.hasTrait() || this.hasTrait() +/** + * A shape is constrained if: + * + * - it has a constraint trait, or; + * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. + * + * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is + * a constrained shape is _not_ constrained. + * + * At the moment the only supported constraint trait is `required`, which can only be attached to structure member shapes. + */ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The only reason why the functions in this file have @@ -33,7 +47,7 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } else -> { -// this.hasConstraintTrait() + // this.hasConstraintTrait() false } } @@ -86,6 +100,7 @@ fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolPro // TODO Callers should use `MemberShape.canReachConstrainedShape`, and this function should be inlined. fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = when (val targetShape = model.expectShape(this.target)) { + // TODO Use is CollectionShape is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) is SetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) From 80ec70539220783fe65cd89877a63994ac969ddd Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 17:45:10 +0200 Subject: [PATCH 061/255] replace DFS in Constraints.kt in favor of Walker implementation --- .../smithy/rust/codegen/smithy/Constraints.kt | 73 ++++--------------- 1 file changed, 15 insertions(+), 58 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index f93864812d..fb9a48ae53 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -2,6 +2,7 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape @@ -15,8 +16,6 @@ import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.util.hasTrait -// TODO Unit test these functions and then refactor to use a `Walker` instead of hand-rolling our own DFS. - /** * A shape has a constraint trait if it has one of these traits attached. */ @@ -52,47 +51,17 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { } } -fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { - if (this.isConstrained(symbolProvider)) { - return true - } - - return this.members().map { model.expectShape(it.target) }.any { - it.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(it, model, symbolProvider, visited) - } -} +fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } -fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { - if (this.isConstrained(symbolProvider)) { - return true - } - - val member = model.expectShape(this.member.target) - - return member.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(member, model, symbolProvider, visited) -} +fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) -fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider, visited: Set = emptySet()): Boolean { - if (this.isConstrained(symbolProvider)) { - return true - } - - val key = model.expectShape(this.key.target) - val value = model.expectShape(this.value.target) - - return key.isConstrained(symbolProvider) || value.isConstrained(symbolProvider) || unconstrainedShapeCanReachConstrainedShape(value, model, symbolProvider, visited) -} - -fun MemberShape.requiresNewtype() = - // Note that member shapes whose only constraint trait is `required` do not require a newtype. - this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() +fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = this.isConstrained(symbolProvider) || this.targetCanReachConstrainedShape(model, symbolProvider) @@ -100,28 +69,16 @@ fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolPro // TODO Callers should use `MemberShape.canReachConstrainedShape`, and this function should be inlined. fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = when (val targetShape = model.expectShape(this.target)) { - // TODO Use is CollectionShape - is ListShape -> targetShape.asListShape().get().canReachConstrainedShape(model, symbolProvider) - is SetShape -> targetShape.asSetShape().get().canReachConstrainedShape(model, symbolProvider) + is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) else -> false } -private fun unconstrainedShapeCanReachConstrainedShape(shape: Shape, model: Model, symbolProvider: SymbolProvider, visited: Set): Boolean { - check(!shape.isConstrained(symbolProvider)) { "This function can only be called with unconstrained shapes" } - - if (visited.contains(shape)) { - return false - } - - val newVisited = setOf(shape).plus(visited) - - return when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) - is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) - is MapShape -> shape.canReachConstrainedShape(model, symbolProvider, newVisited) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. - else -> false - } -} +fun MemberShape.requiresNewtype() = + // Note that member shapes whose only constraint trait is `required` do not require a newtype. + this.hasTrait() || + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() \ No newline at end of file From fde11e9306221dbd60f962c7fc06d6911677d713 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 10 May 2022 19:06:04 +0200 Subject: [PATCH 062/255] refactor renderBuilderMemberFn --- .../generators/ServerBuilderGenerator.kt | 36 ++++--------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 259a750760..a08107424c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -228,35 +228,13 @@ class ServerBuilderGenerator( if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape - // TODO Refactor this. 3 conditions (isOptional, hasBox, constrainedTypeHoldsFinalType) - if (symbol.isOptional()) { - if (hasBox) { - if (constrainedTypeHoldsFinalType) { - rust("input.map(|v| Box::new($maybeConstrainedConstrained(*v)))") - } else { - rust("input.map(|v| Box::new($maybeConstrainedConstrained((*v).into())))") - } - } else { - if (constrainedTypeHoldsFinalType) { - rust("input.map(|v| $maybeConstrainedConstrained(v))") - } else { - rust("input.map(|v| $maybeConstrainedConstrained(v.into()))") - } - } - } else { - if (hasBox) { - // TODO Add a protocol test testing this branch. - if (constrainedTypeHoldsFinalType) { - rust("Box::new($maybeConstrainedConstrained(*input))") - } else { - rust("Box::new($maybeConstrainedConstrained((*input).into()))") - } - } else { - if (constrainedTypeHoldsFinalType) { - rust("$maybeConstrainedConstrained(input)") - } else { - rust("$maybeConstrainedConstrained(input.into())") - } + // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). + var varExpr = if (symbol.isOptional()) "v" else "input" + if (hasBox) varExpr = "*$varExpr" + if (!constrainedTypeHoldsFinalType) varExpr = "($varExpr).into()" + conditionalBlock("input.map(|v| ", ")", conditional = symbol.isOptional()) { + conditionalBlock("Box::new(", ")", conditional = hasBox) { + rust("$maybeConstrainedConstrained($varExpr)") } } } else { From f81550c642560224b4f83ac2f9da93e74d20d8a8 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 11 May 2022 13:19:25 +0200 Subject: [PATCH 063/255] move Constrained trait impl to Constrained* generators --- .../rust/codegen/server/smithy/ServerCodegenVisitor.kt | 2 ++ .../smithy/generators/ConstrainedListGenerator.kt | 10 ++++++++++ .../smithy/generators/ConstrainedMapGenerator.kt | 10 ++++++++++ .../smithy/generators/UnconstrainedListGenerator.kt | 7 ------- .../smithy/generators/UnconstrainedMapGenerator.kt | 6 ------ .../amazon/smithy/rust/codegen/smithy/Constraints.kt | 1 + 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index e2b1fd7161..82343bdf58 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -226,6 +226,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ConstrainedListGenerator( model, symbolProvider, + unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, writer, shape @@ -255,6 +256,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ConstrainedMapGenerator( model, symbolProvider, + unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, writer, shape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index d59cf0b620..4634e15695 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -12,7 +12,9 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape // TODO Docs @@ -20,6 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape class ConstrainedListGenerator( val model: Model, val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: ListShape @@ -29,6 +32,7 @@ class ConstrainedListGenerator( val symbol = symbolProvider.toSymbol(shape) val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) @@ -43,6 +47,10 @@ class ConstrainedListGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) Vec<#{InnerConstrainedSymbol}>); + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + impl From<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { Self(v.into_iter().map(|item| item.into()).collect()) @@ -56,6 +64,8 @@ class ConstrainedListGenerator( } """, "InnerConstrainedSymbol" to innerConstrainedSymbol, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, "Symbol" to symbol, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 68d2fb5489..6a7becb0f8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -16,7 +16,9 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait @@ -25,6 +27,7 @@ import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait class ConstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: MapShape @@ -33,6 +36,7 @@ class ConstrainedMapGenerator( check(shape.canReachConstrainedShape(model, symbolProvider)) val symbol = symbolProvider.toSymbol(shape) + val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name @@ -58,6 +62,10 @@ class ConstrainedMapGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + impl From<#{Symbol}> for $name { fn from(hm: #{Symbol}) -> Self { Self(hm.into_iter().map(|(k, v)| (k, v.into())).collect()) @@ -72,6 +80,8 @@ class ConstrainedMapGenerator( """, "KeySymbol" to keySymbol, "ValueSymbol" to valueSymbol, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, "Symbol" to symbol, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index 226289605e..e34d311367 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -41,7 +40,6 @@ class UnconstrainedListGenerator( val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - // TODO Move implementation of ConstrainedTrait to the constrained module. // TODO Don't be lazy and don't use `Result<_, >`. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( @@ -49,10 +47,6 @@ class UnconstrainedListGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) Vec<#{InnerUnconstrainedSymbol}>); - impl #{ConstrainedTrait} for #{ConstrainedSymbol} { - type Unconstrained = $name; - } - impl From<$name> for #{MaybeConstrained} { fn from(value: $name) -> Self { Self::Unconstrained(value) @@ -83,7 +77,6 @@ class UnconstrainedListGenerator( "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, "ConstrainedSymbol" to constrainedSymbol, "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 7901e6c928..6908c83fad 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -72,10 +71,6 @@ class UnconstrainedMapGenerator( ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); - impl #{ConstrainedTrait} for #{ConstrainedSymbol} { - type Unconstrained = $name; - } - impl From<$name> for #{MaybeConstrained} { fn from(value: $name) -> Self { Self::Unconstrained(value) @@ -111,7 +106,6 @@ class UnconstrainedMapGenerator( *constraintViolationCodegenScope, "ConstrainedSymbol" to constrainedSymbol, "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), ) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index fb9a48ae53..791264f763 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -27,6 +27,7 @@ fun Shape.hasConstraintTrait() = // this.hasTrait() || this.hasTrait() +// TODO Maybe we should rename this to `isDirectlyConstrained`. /** * A shape is constrained if: * From d7b60711d44dfe61a7b44efa508a56c602994090 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 11 May 2022 19:20:53 +0200 Subject: [PATCH 064/255] Length constraint trait on map shapes implemented --- codegen-server-test/model/simple.smithy | 45 +++++----- .../PublicConstrainedShapeSymbolProvider.kt | 81 ++++++++++++++++++ .../server/smithy/RustCodegenServerPlugin.kt | 2 + .../server/smithy/ServerCodegenVisitor.kt | 40 ++++++--- .../PublicConstrainedMapGenerator.kt | 83 +++++++++++++++++++ .../generators/ServerBuilderGenerator.kt | 18 +++- .../generators/UnconstrainedMapGenerator.kt | 79 ++++++++++++------ .../rust/codegen/smithy/CodegenDelegator.kt | 19 +++-- .../smithy/rust/codegen/smithy/Constraints.kt | 1 + .../rust/codegen/smithy/SymbolVisitor.kt | 43 +++++----- 10 files changed, 323 insertions(+), 88 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index a9bbf9ddf9..f9f920d925 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -45,15 +45,15 @@ structure ConA { conBMap: ConBMap } -structure ConB { - @required - nice: String, - @required - int: Integer, - - optNice: String, - optInt: Integer -} +// structure ConB { +// @required +// nice: String, +// @required +// int: Integer, +// +// optNice: String, +// optInt: Integer +// } // structure RecursiveShapesInputOutput { // nested: RecursiveShapesInputOutputNested1 @@ -78,9 +78,9 @@ structure ConB { // foo: ValidList // } -list ConBList { - member: AnotherList -} +// list ConBList { +// member: AnotherList +// } // list ConBList2 { // member: ConB @@ -90,9 +90,9 @@ list ConBList { // member: AnotherList // } // -list AnotherList { - member: ConB -} +// list AnotherList { +// member: ConB +// } // // set ConBSet { // member: AnotherSet @@ -102,13 +102,16 @@ list AnotherList { // member: String // } // + +@length(min: 1, max: 69) map ConBMap { key: String, - value: AnotherMap + //value: AnotherMap + value: String } // -map AnotherMap { - key: String, - value: ConBList - //value: ConB -} +// map AnotherMap { +// key: String, +// value: ConBList +// //value: ConB +// } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt new file mode 100644 index 0000000000..9aec1ab3a8 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.smithy.Models +import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.contextName +import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.locatedIn +import software.amazon.smithy.rust.codegen.smithy.makeOptional +import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.smithy.symbolBuilder +import software.amazon.smithy.rust.codegen.util.hasTrait +import software.amazon.smithy.rust.codegen.util.toPascalCase + +// TODO Unit tests. +class PublicConstrainedShapeSymbolProvider( + private val base: RustSymbolProvider, + private val model: Model, + private val serviceShape: ServiceShape, +) : WrappingSymbolProvider(base) { + private val nullableIndex = NullableIndex.of(model) + + private fun publicConstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape is MapShape) + + val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) + return symbolBuilder(shape, rustType).locatedIn(Models).build() + } + + override fun toSymbol(shape: Shape): Symbol { + if (!shape.isMemberShape && !shape.isConstrained(base)) { + return base.toSymbol(shape) + } + + return when (shape) { + is MapShape -> { + check(shape.hasTrait()) { "Only the `length` constraint trait can be applied to maps" } + publicConstrainedSymbolForCollectionOrMapShape(shape) + } + is MemberShape -> { + // TODO member shapes can have constraint traits. + val target = model.expectShape(shape.target) + val targetSymbol = this.toSymbol(target) + // Handle boxing first so we end up with `Option>`, not `Box>`. + handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + } + else -> base.toSymbol(shape) + } + } + + // TODO These are copied from `SymbolVisitor.kt` + private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = + if (shape.hasTrait()) { + symbol.makeRustBoxed() + } else symbol + + private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = + if (member.isRequired) { + symbol + } else if (nullableIndex.isNullable(member)) { + symbol.makeOptional() + } else { + symbol + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 76dcbfd13e..c5c7e6ad09 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -59,6 +59,8 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) + // TODO Docs + .let { PublicConstrainedShapeSymbolProvider(it, model, serviceShape) } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 82343bdf58..1c2bc45b2e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -29,12 +30,14 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode +import software.amazon.smithy.rust.codegen.smithy.Constrained import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.smithy.Unconstrained import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator @@ -44,6 +47,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator +import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.smithy.transformers.AddErrorMessage @@ -76,8 +80,8 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val codegenContext: CodegenContext private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator - private val unconstrainedModule = RustModule.private("unconstrained", "Unconstrained types for constrained shapes.") - private val constrainedModule = RustModule.private("constrained", "Constrained types for constrained shapes.") + private val unconstrainedModule = RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") + private val constrainedModule = RustModule.private(Constrained.namespace, "Constrained types for constrained shapes.") private val shapesReachableFromOperationInputs: Set init { @@ -236,32 +240,46 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun mapShape(shape: MapShape) { - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { - logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") - - rustCrate.withModule(unconstrainedModule) { writer -> - UnconstrainedMapGenerator( + if (shape.isConstrained(symbolProvider)) { + rustCrate.useShapeWriter(shape) { writer -> + PublicConstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape ).render() } + } - logger.info("[rust-server-codegen] Generating a constrained type for map $shape") - rustCrate.withModule(constrainedModule) { writer -> - ConstrainedMapGenerator( + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { + logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") + rustCrate.withModule(unconstrainedModule) { writer -> + UnconstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, + constraintViolationSymbolProvider, writer, shape ).render() } + + if (!shape.isConstrained(symbolProvider)) { + logger.info("[rust-server-codegen] Generating a constrained type for map $shape") + rustCrate.withModule(constrainedModule) { writer -> + ConstrainedMapGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, + writer, + shape + ).render() + } + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt new file mode 100644 index 0000000000..589b728c51 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.util.expectTrait + +// TODO Docs +// TODO Unit tests +class PublicConstrainedMapGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val writer: RustWriter, + val shape: MapShape +) { + fun render() { + val lengthTrait = shape.expectTrait() + + val name = symbolProvider.toSymbol(shape).name + val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" + val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) + + val minCondition = lengthTrait.min.map { "$it <= length" } + val maxCondition = lengthTrait.max.map { "length <= $it" } + val condition = if (minCondition.isPresent && maxCondition.isPresent) { + "${minCondition.get()} && ${maxCondition.get()}" + } else if (minCondition.isPresent) { + minCondition.get() + } else { + maxCondition.get() + } + + // TODO Docs for everything. + writer.rustTemplate( + """ + ##[derive(Debug, Clone, PartialEq)] + pub struct $name($inner); + + impl $name { + pub fn parse(value: $inner) -> Result { + use std::convert::TryFrom; + Self::try_from(value) + } + } + + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + + impl std::convert::TryFrom<$inner> for $name { + type Error = #{ConstraintViolation}; + + fn try_from(value: $inner) -> Result { + let length = value.len(); + if $condition { + Ok(Self(value)) + } else { + Err(#{ConstraintViolation}::Length(length)) + } + } + } + """, + "KeySymbol" to symbolProvider.toSymbol(model.expectShape(shape.key.target)), + "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedShapeSymbolProvider.toSymbol(shape), + "ConstraintViolation" to constraintViolation + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index a08107424c..215149f8d8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -227,7 +228,8 @@ class ServerBuilderGenerator( conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape + val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape || + memberHasConstraintTraitOrTargetHasConstraintTrait(member) // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" @@ -397,15 +399,22 @@ class ServerBuilderGenerator( ) } + private fun memberHasConstraintTraitOrTargetHasConstraintTrait(member: MemberShape) = + member.hasConstraintTrait() || (model.expectShape(member.target).hasConstraintTrait()) + /** * Returns the symbol for a builder's member. * All builder members are optional, but only some are `Option`s where `T` needs to be constrained. */ private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { - val strippedOption = constrainedShapeSymbolProvider!!.toSymbol(member) - // Strip the `Option` in case the member is not `required`. - .mapRustType { it.stripOuter() } + val strippedOption = if (memberHasConstraintTraitOrTargetHasConstraintTrait(member)) { + symbolProvider.toSymbol(member) + } else { + constrainedShapeSymbolProvider!!.toSymbol(member) + } + // Strip the `Option` in case the member is not `required`. + .mapRustType { it.stripOuter() } val hadBox = strippedOption.isRustBoxed() strippedOption @@ -446,6 +455,7 @@ class ServerBuilderGenerator( for (member in members) { val memberName = symbolProvider.toMemberName(member) val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape + || memberHasConstraintTraitOrTargetHasConstraintTrait(member) withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 6908c83fad..3531231eb7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -11,35 +11,47 @@ import software.amazon.smithy.model.shapes.MapShape 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.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Docs class UnconstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: MapShape ) { + private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + private val name = symbol.name + private val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name + private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + private val valueShape = model.expectShape(shape.value.target) + private val constrainedSymbol = if (shape.isConstrained(symbolProvider)) { + symbolProvider.toSymbol(shape) + } else { + constrainedShapeSymbolProvider.toSymbol(shape) + } + fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() - val name = symbol.name - val keyShape = model.expectShape(shape.key.target, StringShape::class.java) - val valueShape = model.expectShape(shape.value.target) val keySymbol = if (isKeyConstrained(keyShape)) { unconstrainedShapeSymbolProvider.toSymbol(keyShape) } else { @@ -50,8 +62,6 @@ class UnconstrainedMapGenerator( } else { symbolProvider.toSymbol(valueShape) } - val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) - val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val constraintViolationCodegenScope = listOfNotNull( if (isKeyConstrained(keyShape)) { "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) @@ -65,6 +75,8 @@ class UnconstrainedMapGenerator( }, ).toTypedArray() + // TODO Docs for ConstraintViolation. + // TODO KeyConstraintViolation and ValueConstraintViolation need to be `#[doc(hidden)]`. writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -79,34 +91,51 @@ class UnconstrainedMapGenerator( ##[derive(Debug, PartialEq)] pub enum $constraintViolationName { + ${ if (shape.hasTrait()) "Length(usize)," else "" } ${ if (isKeyConstrained(keyShape)) "Key(#{KeyConstraintViolationSymbol})," else "" } ${ if (isValueConstrained(valueShape)) "Value(#{ValueConstraintViolationSymbol})," else "" } } - - impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { - type Error = $constraintViolationName; - - fn try_from(value: $name) -> Result { - let res: Result<_, Self::Error> = value - .0 + """, + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + *constraintViolationCodegenScope, + "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + ) + + renderTryFromUnconstrainedForConstrained(this) + } + } + + private fun renderTryFromUnconstrainedForConstrained(writer: RustWriter) { + writer.rustBlock("impl std::convert::TryFrom<$name> for #{T}", constrainedSymbol) { + rust("type Error = $constraintViolationName;") + + rustBlock("fn try_from(value: $name) -> Result") { + if (listOf(keyShape, valueShape).any { isValueConstrained(it) }) { + rust( + """ + let res: Result<_, Self::Error> = value.0 .into_iter() .map(|(k, v)| { use std::convert::TryInto; - ${ if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(|err| Self::Error::Key(err))?;" else "" } - ${ if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(|err| Self::Error::Value(err))?;" else "" } + ${if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(|err| Self::Error::Key(err))?;" else ""} + ${if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(|err| Self::Error::Value(err))?;" else ""} Ok((k, v)) }) .collect(); - res.map(|inner| Self(inner)) - } + let hm = res?; + """ + ) + } else { + rust("let hm = value.0;") } - """, - "KeySymbol" to keySymbol, - "ValueSymbol" to valueSymbol, - *constraintViolationCodegenScope, - "ConstrainedSymbol" to constrainedSymbol, - "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), - ) + + if (shape.isConstrained(symbolProvider)) { + rust("Self::try_from(hm)") + } else { + rust("Self(hm)") + } + } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index d1dd52665e..99cb0f97e1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -149,16 +149,23 @@ open class RustCrate( } } +val ErrorsModule = RustModule.public("error", documentation = "All error types that operations can return.") +val OperationsModule = RustModule.public("operation", documentation = "All operations that this crate can perform.") +val ModelsModule = RustModule.public("model", documentation = "Data structures used by operation inputs/outputs.") +val InputsModule = RustModule.public("input", documentation = "Input structures for operations.") +val OutputsModule = RustModule.public("output", documentation = "Output structures for operations.") +val ConfigModule = RustModule.public("config", documentation = "Client configuration.") + /** * Allowlist of modules that will be exposed publicly in generated crates */ val DefaultPublicModules = setOf( - RustModule.public("error", documentation = "All error types that operations can return."), - RustModule.public("operation", documentation = "All operations that this crate can perform."), - RustModule.public("model", documentation = "Data structures used by operation inputs/outputs."), - RustModule.public("input", documentation = "Input structures for operations."), - RustModule.public("output", documentation = "Output structures for operations."), - RustModule.public("config", documentation = "Client configuration."), + ErrorsModule, + OperationsModule, + ModelsModule, + InputsModule, + OutputsModule, + ConfigModule ).associateBy { it.name } /** diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 791264f763..0c8b9c3fe1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -46,6 +46,7 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { // `required`, so we can't use `member.isOptional` here. this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } + is MapShape -> this.hasConstraintTrait() else -> { // this.hasConstraintTrait() false diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 7aacc9a2b8..ce82174745 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -84,12 +84,12 @@ data class SymbolLocation(val namespace: String) { val filename = "$namespace.rs" } -val Models = SymbolLocation("model") -val Errors = SymbolLocation("error") -val Operations = SymbolLocation("operation") +val Models = SymbolLocation(ModelsModule.name) +val Errors = SymbolLocation(ErrorsModule.name) +val Operations = SymbolLocation(OperationsModule.name) val Serializers = SymbolLocation("serializer") -val Inputs = SymbolLocation("input") -val Outputs = SymbolLocation("output") +val Inputs = SymbolLocation(InputsModule.name) +val Outputs = SymbolLocation(OutputsModule.name) val Unconstrained = SymbolLocation("unconstrained") val Constrained = SymbolLocation("constrained") @@ -189,6 +189,18 @@ fun SymbolProvider.wrapOptional(member: MemberShape, value: String): String = va */ fun SymbolProvider.toOptional(member: MemberShape, value: String): String = value.letIf(!toSymbol(member).isOptional()) { "Some($value)" } +/** + * Services can rename their contained shapes. See https://awslabs.github.io/smithy/1.0/spec/core/model.html#service + * specifically, `rename` + */ +fun Shape.contextName(serviceShape: ServiceShape?): String { + return if (serviceShape != null) { + id.getName(serviceShape) + } else { + id.name + } +} + /** * Base converter from `Shape` to `Symbol`. Shapes are the direct contents of the `Smithy` model. `Symbols` carry information * about Rust types, namespaces, dependencies, metadata as well as other information required to render a symbol. @@ -209,18 +221,6 @@ class SymbolVisitor( return shape.accept(this) } - /** - * Services can rename their contained shapes. See https://awslabs.github.io/smithy/1.0/spec/core/model.html#service - * specifically, `rename` - */ - private fun Shape.contextName(): String { - return if (serviceShape != null) { - id.getName(serviceShape) - } else { - id.name - } - } - /** * Return the name of a given `enum` variant. Note that this refers to `enum` in the Smithy context * where enum is a trait that can be applied to [StringShape] and not in the Rust context of an algebraic data type. @@ -275,7 +275,8 @@ class SymbolVisitor( override fun doubleShape(shape: DoubleShape): Symbol = simpleShape(shape) override fun stringShape(shape: StringShape): Symbol { return if (shape.hasTrait()) { - symbolBuilder(shape, RustType.Opaque(shape.contextName().toPascalCase())).locatedIn(Models).build() + val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) + symbolBuilder(shape, rustType).locatedIn(Models).build() } else { simpleShape(shape) } @@ -322,7 +323,7 @@ class SymbolVisitor( return symbolBuilder( shape, RustType.Opaque( - shape.contextName() + shape.contextName(serviceShape) .replaceFirstChar { it.uppercase() } ) ) @@ -342,7 +343,7 @@ class SymbolVisitor( val isError = shape.hasTrait() val isInput = shape.hasTrait() val isOutput = shape.hasTrait() - val name = shape.contextName().toPascalCase().letIf(isError && config.codegenConfig.renameExceptions) { + val name = shape.contextName(serviceShape).toPascalCase().letIf(isError && config.codegenConfig.renameExceptions) { it.replace("Exception", "Error") } val builder = symbolBuilder(shape, RustType.Opaque(name)) @@ -355,7 +356,7 @@ class SymbolVisitor( } override fun unionShape(shape: UnionShape): Symbol { - val name = shape.contextName().toPascalCase() + val name = shape.contextName(serviceShape).toPascalCase() val builder = symbolBuilder(shape, RustType.Opaque(name)).locatedIn(Models) return builder.build() From c9b84c81fc93228105520efd900f44304e2730d2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 11 May 2022 21:01:32 +0200 Subject: [PATCH 065/255] Length constraint trait on string shapes implemented --- codegen-server-test/model/simple.smithy | 6 +- .../ConstraintViolationSymbolProvider.kt | 17 +++- .../PublicConstrainedShapeSymbolProvider.kt | 10 ++- .../server/smithy/RustCodegenServerPlugin.kt | 9 +- .../server/smithy/ServerCodegenVisitor.kt | 58 ++++++++++-- .../generators/ConstrainedMapGenerator.kt | 7 +- .../PublicConstrainedStringGenerator.kt | 89 +++++++++++++++++++ .../generators/UnconstrainedMapGenerator.kt | 15 ++-- .../smithy/ConstrainedShapeSymbolProvider.kt | 1 + .../smithy/rust/codegen/smithy/Constraints.kt | 5 ++ 10 files changed, 192 insertions(+), 25 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index f9f920d925..18784a2f61 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -107,8 +107,12 @@ structure ConA { map ConBMap { key: String, //value: AnotherMap - value: String + value: NiceString } + +@length(min: 1, max: 10) +string NiceString + // // map AnotherMap { // key: String, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 05a43ad51b..198761e721 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -12,13 +12,16 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.SetShape 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.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.smithy.Models import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.Unconstrained import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.rustType @@ -88,7 +91,19 @@ class ConstraintViolationSymbolProvider( .definitionFile(Unconstrained.filename) .build() } - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. + is StringShape -> { + check(shape.isConstrained(base)) + + val namespace = "crate::${Models.namespace}::${RustReservedWords.escapeIfNeeded(shape.contextName(serviceShape).toSnakeCase())}" + val rustType = RustType.Opaque(constraintViolationName, namespace) + Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(Models.filename) + .build() + } + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other simple shapes can have constraint traits. else -> base.toSymbol(shape) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt index 9aec1ab3a8..221218cdf8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape 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.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.Models @@ -28,6 +29,7 @@ import software.amazon.smithy.rust.codegen.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase +// TODO Docs. This symbol provider is wrapped by the other ones. // TODO Unit tests. class PublicConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, @@ -54,17 +56,21 @@ class PublicConstrainedShapeSymbolProvider( publicConstrainedSymbolForCollectionOrMapShape(shape) } is MemberShape -> { - // TODO member shapes can have constraint traits. + // TODO member shapes can have constraint traits (constraint trait precedence). val target = model.expectShape(shape.target) val targetSymbol = this.toSymbol(target) // Handle boxing first so we end up with `Option>`, not `Box>`. handleOptionality(handleRustBoxing(targetSymbol, shape), shape) } + is StringShape -> { + val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) + symbolBuilder(shape, rustType).locatedIn(Models).build() + } else -> base.toSymbol(shape) } } - // TODO These are copied from `SymbolVisitor.kt` + // TODO The following two methods have been copied from `SymbolVisitor.kt` private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = if (shape.hasTrait()) { symbol.makeRustBoxed() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index c5c7e6ad09..318a702e90 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -56,15 +56,14 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { fun baseSymbolProvider( model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig + symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig, + publicConstrainedShapesEnabled: Boolean = true ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs - .let { PublicConstrainedShapeSymbolProvider(it, model, serviceShape) } + .let { if (publicConstrainedShapesEnabled) PublicConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { - EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) - } + .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) .let { StreamingShapeSymbolProvider(it, model) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 1c2bc45b2e..698d82610a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedMapGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -33,6 +34,7 @@ import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.Constrained import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules +import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider @@ -57,6 +59,7 @@ import software.amazon.smithy.rust.codegen.smithy.transformers.RecursiveShapeBox import software.amazon.smithy.rust.codegen.smithy.transformers.RemoveEventStreamOperations import software.amazon.smithy.rust.codegen.util.CommandFailed import software.amazon.smithy.rust.codegen.util.getTrait +import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.runCommand import java.util.logging.Logger @@ -80,8 +83,10 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val codegenContext: CodegenContext private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator - private val unconstrainedModule = RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") - private val constrainedModule = RustModule.private(Constrained.namespace, "Constrained types for constrained shapes.") + private val unconstrainedModule = + RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") + private val constrainedModule = + RustModule.private(Constrained.namespace, "Constrained types for constrained shapes.") private val shapesReachableFromOperationInputs: Set init { @@ -104,9 +109,22 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: protocolGeneratorFactory = generator model = generator.transformModel(codegenDecorator.transformModel(service, baseModel)) val baseProvider = RustCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) + // TODO No `CodegenDecorator` is altering the symbol provider, so we might as well remove the `symbolProvider` + // method from the `RustCodegenDecorator` interface. symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) - unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( + codegenDecorator.symbolProvider( + generator.symbolProvider( + model, RustCodegenServerPlugin.baseSymbolProvider( + model, + service, + symbolVisitorConfig, + publicConstrainedShapesEnabled = false + ) + ) + ), model, service + ) constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) @@ -211,7 +229,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun listShape(shape: ListShape) { - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + model, + symbolProvider + ) + ) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") rustCrate.withModule(unconstrainedModule) { writer -> UnconstrainedListGenerator( @@ -253,7 +275,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape(model, symbolProvider)) { + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + model, + symbolProvider + ) + ) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") rustCrate.withModule(unconstrainedModule) { writer -> UnconstrainedMapGenerator( @@ -289,12 +315,32 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - logger.info("[rust-server-codegen] Generating an enum $shape") + check(!(shape.hasTrait() && shape.isConstrained(symbolProvider))) { + """ + String shape has an `enum` trait and another constraint trait. This is valid according to the Smithy spec + v1 IDL, but it makes no sense: https://github.com/awslabs/smithy/issues/1121 + """ + } + shape.getTrait()?.also { enum -> + logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> EnumGenerator(model, symbolProvider, writer, shape, enum).render() } } + + if (shape.isConstrained(symbolProvider)) { + logger.info("[rust-server-codegen] Generating a constrained string $shape") + rustCrate.withModule(ModelsModule) { writer -> + PublicConstrainedStringGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape + ).render() + } + } } /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 6a7becb0f8..fe0a9ba408 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -20,7 +20,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.isConstrained // TODO Docs // TODO Unit tests @@ -88,13 +88,14 @@ class ConstrainedMapGenerator( } // TODO These are copied from `UnconstrainedMapGenerator.kt`. - private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() + private fun isKeyConstrained(shape: StringShape) = shape.isConstrained(symbolProvider) private fun isValueConstrained(shape: Shape): Boolean = when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. + is StringShape -> shape.isConstrained(symbolProvider) + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. else -> false } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt new file mode 100644 index 0000000000..9f80150400 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.util.expectTrait +import software.amazon.smithy.rust.codegen.util.toSnakeCase + +// TODO Docs +// TODO Unit tests +class PublicConstrainedStringGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val writer: RustWriter, + val shape: StringShape +) { + fun render() { + val lengthTrait = shape.expectTrait() + + val name = symbolProvider.toSymbol(shape).name + val inner = "String" + val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) + + val minCondition = lengthTrait.min.map { "$it <= length" } + val maxCondition = lengthTrait.max.map { "length <= $it" } + val condition = if (minCondition.isPresent && maxCondition.isPresent) { + "${minCondition.get()} && ${maxCondition.get()}" + } else if (minCondition.isPresent) { + minCondition.get() + } else { + maxCondition.get() + } + + // TODO Docs for everything. + // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the + // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. + // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait + writer.rustTemplate( + """ + ##[derive(Debug, Clone, PartialEq)] + pub struct $name($inner); + + impl $name { + pub fn parse(value: $inner) -> Result { + use std::convert::TryFrom; + Self::try_from(value) + } + } + + impl #{ConstrainedTrait} for $name { + type Unconstrained = $inner; + } + + pub mod ${name.toSnakeCase()} { + ##[derive(Debug, PartialEq)] + pub enum ConstraintViolation { + Length(usize), + } + } + + impl std::convert::TryFrom<$inner> for $name { + type Error = #{ConstraintViolation}; + + fn try_from(value: $inner) -> Result { + let length = value.chars().count(); + if $condition { + Ok(Self(value)) + } else { + Err(#{ConstraintViolation}::Length(length)) + } + } + } + """, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "ConstraintViolation" to constraintViolation + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 3531231eb7..b16bf131ac 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -112,9 +112,9 @@ class UnconstrainedMapGenerator( rustBlock("fn try_from(value: $name) -> Result") { if (listOf(keyShape, valueShape).any { isValueConstrained(it) }) { - rust( + rustTemplate( """ - let res: Result<_, Self::Error> = value.0 + let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { use std::convert::TryInto; @@ -124,7 +124,9 @@ class UnconstrainedMapGenerator( }) .collect(); let hm = res?; - """ + """, + "ConstrainedKeySymbol" to symbolProvider.toSymbol(keyShape), + "ConstrainedValueSymbol" to symbolProvider.toSymbol(valueShape), ) } else { rust("let hm = value.0;") @@ -139,15 +141,14 @@ class UnconstrainedMapGenerator( } } - // TODO This is the correct implementation when we have constraint traits. -// private fun isKeyConstrained(shape: StringShape) = shape.hasConstraintTrait() - private fun isKeyConstrained(shape: StringShape) = false + private fun isKeyConstrained(shape: StringShape) = shape.isConstrained(symbolProvider) private fun isValueConstrained(shape: Shape): Boolean = when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Constraint traits on simple shapes. + is StringShape -> shape.isConstrained(symbolProvider) + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. else -> false } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt index 91e02f3873..c1e78c1ced 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt @@ -128,6 +128,7 @@ class ConstrainedShapeSymbolProvider( } else -> { // TODO Other shape types. + // TODO We should arguably panic here? Since the rest are either public / don't need constrained type. base.toSymbol(shape) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 0c8b9c3fe1..9bc11afe31 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.SetShape 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.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait @@ -28,6 +29,9 @@ fun Shape.hasConstraintTrait() = this.hasTrait() // TODO Maybe we should rename this to `isDirectlyConstrained`. +// TODO Perhaps it's best that we specialize and have `StringShape.isConstrained()`, `MapShape.isConstrained()` etc, +// and we check only for the supported constraint traits and also only check for the ones compatible according to the +// Smithy spec selectors. /** * A shape is constrained if: * @@ -47,6 +51,7 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } is MapShape -> this.hasConstraintTrait() + is StringShape -> this.hasTrait() // TODO For the moment only `length` on string shapes is supported. else -> { // this.hasConstraintTrait() false From fb168a59ef84f64502a73387aeb8c9c2317739e0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 12 May 2022 13:02:34 +0200 Subject: [PATCH 066/255] comment --- .../smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 698d82610a..accebc352b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -317,8 +317,8 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: override fun stringShape(shape: StringShape) { check(!(shape.hasTrait() && shape.isConstrained(symbolProvider))) { """ - String shape has an `enum` trait and another constraint trait. This is valid according to the Smithy spec - v1 IDL, but it makes no sense: https://github.com/awslabs/smithy/issues/1121 + String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy + spec v1 IDL, but it makes no sense: https://github.com/awslabs/smithy/issues/1121 """ } From 0a03b711df175aa655b840529facffe839cf5002 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 00:23:56 +0200 Subject: [PATCH 067/255] fixed when deser of HTTP-bound @length string shapes; sst/svt/srt/server-unit-tests all work --- codegen-server-test/model/simple.smithy | 38 +++++++++--- .../PublicConstrainedShapeSymbolProvider.kt | 45 +++++++++++--- .../server/smithy/ServerCodegenVisitor.kt | 28 ++++++--- .../PublicConstrainedStringGenerator.kt | 45 ++++++++++++-- .../generators/ServerBuilderGenerator.kt | 6 +- .../generators/UnconstrainedMapGenerator.kt | 59 +++++++++++++++++-- .../http/ServerRequestBindingGenerator.kt | 5 +- .../http/ServerResponseBindingGenerator.kt | 3 +- .../ServerHttpBoundProtocolGenerator.kt | 16 +++-- .../codegen/server/smithy/ConstraintsTest.kt | 2 +- .../UnconstrainedShapeSymbolProviderTest.kt | 10 ++-- .../UnconstrainedListGeneratorTest.kt | 3 +- .../UnconstrainedMapGeneratorTest.kt | 3 +- .../rust/codegen/smithy/CodegenContext.kt | 5 +- .../smithy/rust/codegen/smithy/Constraints.kt | 5 +- .../UnconstrainedShapeSymbolProvider.kt | 41 +++++++++---- .../smithy/generators/StructureGenerator.kt | 11 +--- .../smithy/generators/error/ErrorGenerator.kt | 26 ++++++-- .../generators/http/HttpBindingGenerator.kt | 3 +- .../http/RequestBindingGenerator.kt | 3 +- .../http/ResponseBindingGenerator.kt | 3 +- .../rust/codegen/testutil/TestHelpers.kt | 2 +- 22 files changed, 274 insertions(+), 88 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 18784a2f61..041a110432 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -18,6 +18,7 @@ operation AnOperation { // output: RecursiveShapesInputOutput, input: AnOperationInput, output: AnOperationOutput, + /* errors: [MyError] */ } structure AnOperationInput { @@ -42,9 +43,20 @@ structure ConA { // conBSet: ConBSet, - conBMap: ConBMap + // conBMap: ConBMap + + @length(min:4, max:6) + list: LengthList, +} + +@length(min:2, max:8) +list LengthList { + member: LengthString } +@length(min:2, max:8) +string LengthString + // structure ConB { // @required // nice: String, @@ -103,15 +115,23 @@ structure ConA { // } // -@length(min: 1, max: 69) -map ConBMap { - key: String, - //value: AnotherMap - value: NiceString -} +// @length(min: 1, max: 69) +// map ConBMap { +// key: String, +// //value: AnotherMap +// value: NiceString +// } + +// @length(min: 1, max: 10) +// string NiceString -@length(min: 1, max: 10) -string NiceString +// @error("client") +// @retryable +// @httpError(429) +// structure MyError { +// @required +// message: NiceString +// } // // map AnotherMap { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt index 221218cdf8..0fc4fa42e1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.MemberShape 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.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.Models @@ -25,6 +26,7 @@ import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.locatedIn import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase @@ -46,15 +48,7 @@ class PublicConstrainedShapeSymbolProvider( } override fun toSymbol(shape: Shape): Symbol { - if (!shape.isMemberShape && !shape.isConstrained(base)) { - return base.toSymbol(shape) - } - return when (shape) { - is MapShape -> { - check(shape.hasTrait()) { "Only the `length` constraint trait can be applied to maps" } - publicConstrainedSymbolForCollectionOrMapShape(shape) - } is MemberShape -> { // TODO member shapes can have constraint traits (constraint trait precedence). val target = model.expectShape(shape.target) @@ -62,7 +56,42 @@ class PublicConstrainedShapeSymbolProvider( // Handle boxing first so we end up with `Option>`, not `Box>`. handleOptionality(handleRustBoxing(targetSymbol, shape), shape) } + is MapShape -> { + if (shape.isConstrained(base)) { + check(shape.hasTrait()) { "Only the `length` constraint trait can be applied to maps" } + publicConstrainedSymbolForCollectionOrMapShape(shape) + } else { + val keySymbol = this.toSymbol(shape.key) + val valueSymbol = this.toSymbol(shape.value) + symbolBuilder(shape, RustType.HashMap(keySymbol.rustType(), valueSymbol.rustType())) + .addReference(keySymbol) + .addReference(valueSymbol) + .build() + } + } + is CollectionShape -> { + // TODO Both arms return the same because we haven't implemented any constraint trait on collection shapes yet. + if (shape.isConstrained(base)) { + val inner = this.toSymbol(shape.member) + symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() + } else { + val inner = this.toSymbol(shape.member) + symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() + } + } is StringShape -> { + if (!shape.isConstrained(base)) { + return base.toSymbol(shape) + } + + if (shape.hasTrait()) { + // String shape has a constraint trait in addition to the `enum` trait. + // The `enum` trait takes precedence, so the base symbol provider will generate a Rust `enum`. + // We warn about this case in [ServerCodegenVisitor]. + // See https://github.com/awslabs/smithy/issues/1121f for more information. + return base.toSymbol(shape) + } + val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) symbolBuilder(shape, rustType).locatedIn(Models).build() } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index accebc352b..86552ac7be 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -128,7 +128,15 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) - codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, mode = CodegenMode.Server) + codegenContext = CodegenContext( + model, + symbolProvider, + service, + protocol, + settings, + mode = CodegenMode.Server, + unconstrainedShapeSymbolProvider + ) rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) @@ -315,13 +323,6 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - check(!(shape.hasTrait() && shape.isConstrained(symbolProvider))) { - """ - String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy - spec v1 IDL, but it makes no sense: https://github.com/awslabs/smithy/issues/1121 - """ - } - shape.getTrait()?.also { enum -> logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> @@ -329,7 +330,16 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } - if (shape.isConstrained(symbolProvider)) { + if (shape.hasTrait() && shape.isConstrained(symbolProvider)) { + logger.warning( + """ + String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy + spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the + constraints (which it currently does not), not each code generator. + See https://github.com/awslabs/smithy/issues/1121f for more information. + """.trimIndent() + ) + } else if (shape.isConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> PublicConstrainedStringGenerator( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt index 9f80150400..5538999a43 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -8,13 +8,17 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.toSnakeCase +import java.util.* // TODO Docs // TODO Unit tests @@ -28,11 +32,14 @@ class PublicConstrainedStringGenerator( fun render() { val lengthTrait = shape.expectTrait() - val name = symbolProvider.toSymbol(shape).name - val inner = "String" + val symbol = symbolProvider.toSymbol(shape) + val name = symbol.name + val inner = RustType.String.render() val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) - val minCondition = lengthTrait.min.map { "$it <= length" } + val minCondition = if (lengthTrait.min.isPresent && lengthTrait.min.get().toInt() > 0) { + lengthTrait.min.map { "$it <= length" } + } else Optional.empty() val maxCondition = lengthTrait.max.map { "length <= $it" } val condition = if (minCondition.isPresent && maxCondition.isPresent) { "${minCondition.get()} && ${maxCondition.get()}" @@ -43,12 +50,13 @@ class PublicConstrainedStringGenerator( } // TODO Docs for everything. + // TODO Display impl. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait writer.rustTemplate( """ - ##[derive(Debug, Clone, PartialEq)] + ##[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name($inner); impl $name { @@ -62,6 +70,32 @@ class PublicConstrainedStringGenerator( type Unconstrained = $inner; } + impl From<$inner> for #{MaybeConstrained} { + fn from(value: $inner) -> Self { + Self::Unconstrained(value) + } + } + + impl std::ops::Deref for $name { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl AsRef for $name { + fn as_ref(&self) -> &str { + self.0.as_str() + } + } + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + pub mod ${name.toSnakeCase()} { ##[derive(Debug, PartialEq)] pub enum ConstraintViolation { @@ -83,7 +117,8 @@ class PublicConstrainedStringGenerator( } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "ConstraintViolation" to constraintViolation + "ConstraintViolation" to constraintViolation, + "MaybeConstrained" to symbol.wrapMaybeConstrained(), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 215149f8d8..ee2b29b00b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,7 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -210,7 +210,7 @@ class ServerBuilderGenerator( } /** - * Render a `foo` method for to set shape member `foo`. The caller must provide a value with the exact same type + * Render a `foo` method to set shape member `foo`. The caller must provide a value with the exact same type * as the shape member's type. */ private fun renderBuilderMemberFn( @@ -400,7 +400,7 @@ class ServerBuilderGenerator( } private fun memberHasConstraintTraitOrTargetHasConstraintTrait(member: MemberShape) = - member.hasConstraintTrait() || (model.expectShape(member.target).hasConstraintTrait()) + member.isConstrained(symbolProvider) || (model.expectShape(member.target).isConstrained(symbolProvider)) /** * Returns the symbol for a builder's member. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index b16bf131ac..1c393530fb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -32,7 +32,7 @@ class UnconstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: MapShape @@ -111,10 +111,20 @@ class UnconstrainedMapGenerator( rust("type Error = $constraintViolationName;") rustBlock("fn try_from(value: $name) -> Result") { - if (listOf(keyShape, valueShape).any { isValueConstrained(it) }) { + if (isKeyConstrained(keyShape) || isValueConstrained(valueShape)) { + val resolveToNonPublicConstrainedValueType = + isValueConstrained(valueShape) && + !valueShape.isConstrained(symbolProvider) && + !valueShape.isStructureShape + val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { + constrainedShapeSymbolProvider.toSymbol(valueShape) + } else { + symbolProvider.toSymbol(valueShape) + } + rustTemplate( """ - let res: Result, Self::Error> = value.0 + let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { use std::convert::TryInto; @@ -125,9 +135,46 @@ class UnconstrainedMapGenerator( .collect(); let hm = res?; """, - "ConstrainedKeySymbol" to symbolProvider.toSymbol(keyShape), - "ConstrainedValueSymbol" to symbolProvider.toSymbol(valueShape), + "KeySymbol" to symbolProvider.toSymbol(keyShape), + "ConstrainedValueSymbol" to constrainedValueSymbol ) + + val constrainedValueTypeIsNotFinalType = + resolveToNonPublicConstrainedValueType && shape.isConstrained(symbolProvider) + if (constrainedValueTypeIsNotFinalType) { + // The map is constrained. Its value shape reaches a constrained shape, but the value shape itself + // is not directly constrained. The value shape must be an aggregate shape. But it is not a + // structure shape. So it must be a collection or map shape. In this case the type for the value + // shape that implements the `Constrained` trait _does not_ coincide with the regular type the user + // is exposed to. The former will be the `pub(crate)` wrapper tuple type created by a + // `Constrained*Generator`, whereas the latter will be an stdlib container type. Both types are + // isomorphic though, and we can convert between them using `From`, so that's what we do here. + // + // As a concrete example of this particular case, consider the model: + // + // ```smithy + // @length(min: 1) + // map Map { + // key: String, + // value: List, + // } + // + // list List { + // member: NiceString + // } + // + // @length(min:1, max:69) + // string NiceString + // ``` + rustTemplate( + """ + let hm: std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}> = + hm.into_iter().map(|(k, v)| (k, v.into())).collect(); + """, + "KeySymbol" to symbolProvider.toSymbol(keyShape), + "ValueSymbol" to symbolProvider.toSymbol(valueShape) + ) + } } else { rust("let hm = value.0;") } @@ -135,7 +182,7 @@ class UnconstrainedMapGenerator( if (shape.isConstrained(symbolProvider)) { rust("Self::try_from(hm)") } else { - rust("Self(hm)") + rust("Ok(Self(hm))") } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index dd5809902a..5931f6da6d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor @@ -17,9 +18,11 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol class ServerRequestBindingGenerator( protocol: Protocol, codegenContext: CodegenContext, + unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, operationShape: OperationShape ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, unconstrainedShapeSymbolProvider, operationShape) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index 0b9fe0a214..472703f3f2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -18,7 +18,8 @@ class ServerResponseBindingGenerator( codegenContext: CodegenContext, operationShape: OperationShape ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) fun generateAddHeadersFn(shape: Shape): RuntimeType? = httpBindingGenerator.generateAddHeadersFn(shape, HttpMessageType.RESPONSE) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 5f0a2cdf2e..a2566d2a60 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -104,6 +104,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) : ProtocolTraitImplGenerator { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver @@ -495,7 +496,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( * case it will generate response headers for the given error shape. * * It sets three groups of headers in order. Headers from one group take precedence over headers in a later group. - * 1. Headers bound by the `httpHeader` and `httpPrefixHeader` traits. + * 1. Headers bound by the `httpHeader` and `httpPrefixHeader` traits. = null * 2. The protocol-specific `Content-Type` header for the operation. * 3. Additional protocol-specific headers for errors, if [errorShape] is non-null. */ @@ -574,7 +575,12 @@ private class ServerHttpBoundProtocolTraitImplGenerator( inputShape: StructureShape, bindings: List, ) { - val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) + val httpBindingGenerator = ServerRequestBindingGenerator( + protocol, + codegenContext, + codegenContext.unconstrainedShapeSymbolProvider!!, + operationShape + ) val structuredDataParser = protocol.structuredDataParser(operationShape) Attribute.AllowUnusedMut.render(this) rust("let mut input = #T::default();", inputShape.builderSymbol(symbolProvider)) @@ -975,6 +981,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ServerRequestBindingGenerator( protocol, codegenContext, + codegenContext.unconstrainedShapeSymbolProvider!!, operationShape, ) val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) @@ -994,6 +1001,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ServerRequestBindingGenerator( protocol, codegenContext, + codegenContext.unconstrainedShapeSymbolProvider!!, operationShape, ) val deserializer = httpBindingGenerator.generateDeserializePrefixHeadersFn(binding) @@ -1021,7 +1029,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun generateParsePercentEncodedStrAsStringFn(binding: HttpBindingDescriptor): RuntimeType { - val output = symbolProvider.toSymbol(binding.member) + val output = unconstrainedShapeSymbolProvider!!.toSymbol(binding.member) val fnName = generateParseStrFnName(binding) return RuntimeType.forInlineFun(fnName, operationDeserModule) { writer -> writer.rustBlockTemplate( @@ -1035,7 +1043,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ let value = <_>::from(#{PercentEncoding}::percent_decode_str(value).decode_utf8()?.as_ref()); - Ok(${symbolProvider.wrapOptional(binding.member, "value")}) + Ok(${unconstrainedShapeSymbolProvider.wrapOptional(binding.member, "value")}) """.trimIndent(), *codegenScope, ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index 450b2e90b9..870d40e06f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -122,7 +122,7 @@ class ConstraintsTest { @Test fun `it should not detect unsupported constrained traits as constrained`() { - listOf(mapA, structAInt, structAString, myString).forEach { + listOf(structAInt, structAString, myString).forEach { it.isConstrained(symbolProvider) shouldBe false } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index acd3b10580..4740dcea50 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -5,13 +5,13 @@ package software.amazon.smithy.rust.codegen.server.smithy -import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.rustType @@ -79,7 +79,7 @@ class UnconstrainedShapeSymbolProviderTest { } @Test - fun `it should throw if called with a shape that cannot reach a constrained shape`() { + fun `it should delegate to the base symbol provider if called with a shape that cannot reach a constrained shape`() { val model = """ $baseModelString @@ -97,9 +97,9 @@ class UnconstrainedShapeSymbolProviderTest { val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) val listAShape = model.lookup("test#ListA") + val structureBShape = model.lookup("test#StructureB") - shouldThrow { - symbolProvider.toSymbol(listAShape) - } + symbolProvider.toSymbol(structureBShape).rustType().render() shouldBe "crate::model::StructureB" + symbolProvider.toSymbol(listAShape).rustType().render() shouldBe "std::vec::Vec" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt index 1fffd30984..db897c11d9 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -70,12 +70,14 @@ class UnconstrainedListGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { ConstrainedListGenerator( model, symbolProvider, + unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, writer, it @@ -83,7 +85,6 @@ class UnconstrainedListGeneratorTest { } } project.withModule(RustModule.private("unconstrained")) { writer -> - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(listA, listB).forEach { UnconstrainedListGenerator( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 5c0b57ccd2..0fa0b78f81 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -72,12 +72,14 @@ class UnconstrainedMapGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(mapA, mapB).forEach { ConstrainedMapGenerator( model, symbolProvider, + unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, writer, it @@ -85,7 +87,6 @@ class UnconstrainedMapGeneratorTest { } } project.withModule(RustModule.private("unconstrained")) { writer -> - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(mapA, mapB).forEach { UnconstrainedMapGenerator( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt index 7ac2f45e27..3992e569fe 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt @@ -53,6 +53,8 @@ data class CodegenContext( * Some settings are dependent on whether server vs. client codegen is being invoked. */ val mode: CodegenMode, + + val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null ) { constructor( model: Model, @@ -61,7 +63,8 @@ data class CodegenContext( protocol: ShapeId, settings: RustSettings, mode: CodegenMode, - ) : this(model, symbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, mode) + unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null + ) : this(model, symbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, mode, unconstrainedShapeSymbolProvider) /** * The name of the cargo crate to generate e.g. `aws-sdk-s3` diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 9bc11afe31..07570ac86f 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.SetShape 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.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait @@ -51,7 +52,7 @@ fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } is MapShape -> this.hasConstraintTrait() - is StringShape -> this.hasTrait() // TODO For the moment only `length` on string shapes is supported. + is StringShape -> !this.hasTrait() && this.hasTrait() // TODO For the moment only `length` on string shapes is supported. else -> { // this.hasConstraintTrait() false @@ -79,7 +80,7 @@ fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: Sym is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) - else -> false + else -> targetShape.isConstrained(symbolProvider) } fun MemberShape.requiresNewtype() = diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 9ca7f915be..1938250f07 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -50,29 +50,44 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } is ListShape -> { - check(shape.canReachConstrainedShape(model, base)) - - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on list shapes is currently not implemented") + if (shape.canReachConstrainedShape(model, base)) { unconstrainedSymbolForListOrMapShape(shape) } else { - unconstrainedSymbolForListOrMapShape(shape) + base.toSymbol(shape) } +// check(shape.canReachConstrainedShape(model, base)) +// +// if (shape.isConstrained(base)) { +//// TODO("The `length` constraint trait on list shapes is currently not implemented") +// unconstrainedSymbolForListOrMapShape(shape) +// } else { +// unconstrainedSymbolForListOrMapShape(shape) +// } } is MapShape -> { - check(shape.canReachConstrainedShape(model, base)) - - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on map shapes is currently not implemented") + if (shape.canReachConstrainedShape(model, base)) { unconstrainedSymbolForListOrMapShape(shape) } else { - unconstrainedSymbolForListOrMapShape(shape) + base.toSymbol(shape) } +// check(shape.canReachConstrainedShape(model, base)) +// +// if (shape.isConstrained(base)) { +//// TODO("The `length` constraint trait on map shapes is currently not implemented") +// unconstrainedSymbolForListOrMapShape(shape) +// } else { +// unconstrainedSymbolForListOrMapShape(shape) +// } } is StructureShape -> { - check(shape.canReachConstrainedShape(model, base)) - - shape.builderSymbol(base) + if (shape.canReachConstrainedShape(model, base)) { + shape.builderSymbol(base) + } else { + base.toSymbol(shape) + } +// check(shape.canReachConstrainedShape(model, base)) +// +// shape.builderSymbol(base) } // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. else -> base.toSymbol(shape) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 83a78ff71a..7c4e79cc34 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -8,8 +8,6 @@ package software.amazon.smithy.rust.codegen.smithy.generators 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.CollectionShape -import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape @@ -64,14 +62,7 @@ fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: Symb return this.setterName() } - val canReachConstrainedShape = when (val targetShape = model.expectShape(this.target)) { - is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - is StructureShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - else -> false - } - - return if (canReachConstrainedShape) { + return if (this.canReachConstrainedShape(model, symbolProvider)) { "set_${this.memberName.toSnakeCase()}" } else { this.memberName.toSnakeCase() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt index 716fbbe73b..442eb8144e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt @@ -8,10 +8,14 @@ package software.amazon.smithy.rust.codegen.smithy.generators.error 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.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.asDeref +import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -21,6 +25,8 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.letIf +import software.amazon.smithy.rust.codegen.smithy.mapRustType +import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.transformers.errorMessageMember import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.getTrait @@ -70,13 +76,25 @@ class ErrorGenerator( val symbol = symbolProvider.toSymbol(shape) val messageShape = shape.errorMessageMember() val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) + // TODO Why do we always generate a `pub fn message() -> Option<&str> { None }` for `@error` structure shapes, + // even when they don’t have a `message` field? val (returnType, message) = messageShape?.let { - if (symbolProvider.toSymbol(messageShape).isOptional()) { - "Option<&str>" to "self.${symbolProvider.toMemberName(it)}.as_deref()" + val messageSymbol = symbolProvider.toSymbol(it).mapRustType { t -> t.asDeref() } + if (messageSymbol.rustType().stripOuter() is RustType.Opaque) { + // The string shape has a constraint trait that makes its symbol be a wrapper tuple struct. + if (messageSymbol.isOptional()) { + "Option<&${messageSymbol.rustType().stripOuter().render()}>" to "self.${symbolProvider.toMemberName(it)}.as_ref()" + } else { + "&${messageSymbol.rustType().render()}" to "&self.${symbolProvider.toMemberName(it)}" + } } else { - "&str" to "self.${symbolProvider.toMemberName(it)}.as_ref()" + if (messageSymbol.isOptional()) { + messageSymbol.rustType().render() to "self.${symbolProvider.toMemberName(it)}.as_deref()" + } else { + messageSymbol.rustType().render() to "self.${symbolProvider.toMemberName(it)}.as_ref()" + } } - } ?: "Option<&str>" to "None" + } ?: ("Option<&str>" to "None") writer.rustBlock("impl ${symbol.name}") { val retryKindWriteable = shape.modeledRetryKind(error)?.writable(symbolProvider.config().runtimeConfig) if (retryKindWriteable != null) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index c447f59b4a..73d0a784a7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -36,6 +36,7 @@ import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.generators.redactIfNecessary import software.amazon.smithy.rust.codegen.smithy.makeOptional @@ -86,10 +87,10 @@ public enum class HttpMessageType { class HttpBindingGenerator( private val protocol: Protocol, codegenContext: CodegenContext, + val symbolProvider: RustSymbolProvider, private val operationShape: OperationShape ) { private val runtimeConfig = codegenContext.runtimeConfig - private val symbolProvider = codegenContext.symbolProvider private val mode = codegenContext.mode private val model = codegenContext.model private val service = codegenContext.serviceShape diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt index 59ccc23675..2a09bc9b5c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/RequestBindingGenerator.kt @@ -62,7 +62,8 @@ class RequestBindingGenerator( private val symbolProvider = codegenContext.symbolProvider private val runtimeConfig = codegenContext.runtimeConfig private val httpTrait = protocol.httpBindingResolver.httpTrait(operationShape) - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) private val index = HttpBindingIndex.of(model) private val Encoder = CargoDependency.SmithyTypes(runtimeConfig).asType().member("primitive::Encoder") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt index 5652519bfb..deec1a3730 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/ResponseBindingGenerator.kt @@ -17,7 +17,8 @@ class ResponseBindingGenerator( codegenContext: CodegenContext, operationShape: OperationShape ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt index 33144d8d7a..25332cce59 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt @@ -84,7 +84,7 @@ fun testCodegenContext( serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), settings, - mode + mode, ) private const val SmithyVersion = "1.0" From 733d2359d5a33acefa9abce645a621a7284e9c46 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 14:34:51 +0200 Subject: [PATCH 068/255] make clippy happy; unions of structures don't work --- codegen-server-test/model/simple.smithy | 51 ++++++++++++++++--- .../generators/ConstrainedListGenerator.kt | 32 ++++++++++-- .../PublicConstrainedMapGenerator.kt | 12 ++--- .../PublicConstrainedStringGenerator.kt | 15 ++---- .../generators/ServerBuilderGenerator.kt | 4 +- .../generators/UnconstrainedListGenerator.kt | 4 +- .../generators/UnconstrainedMapGenerator.kt | 4 +- 7 files changed, 88 insertions(+), 34 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 041a110432..a5414ccc05 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -45,18 +45,57 @@ structure ConA { // conBMap: ConBMap - @length(min:4, max:6) - list: LengthList, -} + normalString: NormalString, + lengthString: LengthString, + playerAction: PlayerAction, + myEnum: MyEnum -@length(min:2, max:8) -list LengthList { - member: LengthString + // @length(min:4, max:6) + // list: LengthList, } +// @length(min:2, max:8) +// list LengthList { +// member: LengthString +// } + @length(min:2, max:8) string LengthString +string NormalString + +union PlayerAction { + /// Quit the game. + quit: Unit, + + /// Move in a specific direction. + move: DirectedAction, + + /// Jump in a specific direction. + jump: DirectedAction +} + +structure DirectedAction { + @required + direction: Integer +} + +@enum([ + { + value: "t2.nano", + name: "T2_NANO", + }, + { + value: "t2.micro", + name: "T2_MICRO", + }, + { + value: "m256.mega", + name: "M256_MEGA", + } +]) +string MyEnum + // structure ConB { // @required // nice: String, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt index 4634e15695..78e5f0c6ff 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -16,6 +17,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isConstrained // TODO Docs // TODO Unit tests @@ -38,9 +40,21 @@ class ConstrainedListGenerator( val innerShape = model.expectShape(shape.member.target) val innerConstrainedSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) - // The converters are only needed when the constrained type is `pub(crate)`, for the server builder function - // member function to work. - // Note that unless the list holds an aggregate shape, the `.into()` calls are useless. + // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, + // then its corresponding constrained type is the `pub(crate)` wrapper tuple type, which needs converting into + // the public type the user is exposed to. The two types are isomorphic, and we can convert between them using + // `From`. So we track this particular case here in order to iterate over the list's members and convert + // each of them. + // + // Note that we could add the iteration code unconditionally and it would still be correct, but the `into()` calls + // would be useless. Clippy flags this as [`useless_conversion`]. We could deactivate the lint, but it's probably + // best that we just don't emit a useless iteration, lest the compiler not optimize it away, and to make the + // generated code a little bit simpler. + // + // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. + val targetNeedsConstraining = + !innerShape.isConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape.isMapShape) + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -53,13 +67,21 @@ class ConstrainedListGenerator( impl From<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { - Self(v.into_iter().map(|item| item.into()).collect()) + ${ if (targetNeedsConstraining) { + "Self(v.into_iter().map(|item| item.into()).collect())" + } else { + "Self(v)" + } } } } impl From<$name> for #{Symbol} { fn from(v: $name) -> Self { - v.0.into_iter().map(|item| item.into()).collect() + ${ if (targetNeedsConstraining) { + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt index 589b728c51..926f874806 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt @@ -33,14 +33,12 @@ class PublicConstrainedMapGenerator( val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) - val minCondition = lengthTrait.min.map { "$it <= length" } - val maxCondition = lengthTrait.max.map { "length <= $it" } - val condition = if (minCondition.isPresent && maxCondition.isPresent) { - "${minCondition.get()} && ${maxCondition.get()}" - } else if (minCondition.isPresent) { - minCondition.get() + val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { + "(${lengthTrait.min.get()}..=${lengthTrait.max.get()}).contains(&length)" + } else if (lengthTrait.min.isPresent) { + "${lengthTrait.min.get()} <= length" } else { - maxCondition.get() + "length <= ${lengthTrait.max.get()}" } // TODO Docs for everything. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt index 5538999a43..03c8e283fe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.toSnakeCase -import java.util.* // TODO Docs // TODO Unit tests @@ -37,16 +36,12 @@ class PublicConstrainedStringGenerator( val inner = RustType.String.render() val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) - val minCondition = if (lengthTrait.min.isPresent && lengthTrait.min.get().toInt() > 0) { - lengthTrait.min.map { "$it <= length" } - } else Optional.empty() - val maxCondition = lengthTrait.max.map { "length <= $it" } - val condition = if (minCondition.isPresent && maxCondition.isPresent) { - "${minCondition.get()} && ${maxCondition.get()}" - } else if (minCondition.isPresent) { - minCondition.get() + val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { + "(${lengthTrait.min.get()}..=${lengthTrait.max.get()}).contains(&length)" + } else if (lengthTrait.min.isPresent) { + "${lengthTrait.min.get()} <= length" } else { - maxCondition.get() + "length <= ${lengthTrait.max.get()}" } // TODO Docs for everything. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ee2b29b00b..fda4b364d5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -234,7 +234,7 @@ class ServerBuilderGenerator( var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" if (!constrainedTypeHoldsFinalType) varExpr = "($varExpr).into()" - conditionalBlock("input.map(|v| ", ")", conditional = symbol.isOptional()) { + conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { rust("$maybeConstrainedConstrained($varExpr)") } @@ -494,7 +494,7 @@ class ServerBuilderGenerator( }) .map(|res| res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } - .map_err(|err| ConstraintViolation::${constraintViolation.name()}(err)) + .map_err(ConstraintViolation::${constraintViolation.name()}) ) .transpose()? """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index e34d311367..eb43dbbc4a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -68,8 +68,8 @@ class UnconstrainedListGenerator( inner.try_into() }) .collect(); - res.map(|inner| Self(inner)) - .map_err(|err| ConstraintViolation(err)) + res.map(Self) + .map_err(ConstraintViolation) } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 1c393530fb..368a6b2694 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -128,8 +128,8 @@ class UnconstrainedMapGenerator( .into_iter() .map(|(k, v)| { use std::convert::TryInto; - ${if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(|err| Self::Error::Key(err))?;" else ""} - ${if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(|err| Self::Error::Value(err))?;" else ""} + ${if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} + ${if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(Self::Error::Value)?;" else ""} Ok((k, v)) }) .collect(); From d97abfe90159a2cfec45e0df48a756b0e7b3f808 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 15:23:11 +0200 Subject: [PATCH 069/255] make clippy happy about recursive shapes --- codegen-server-test/model/simple.smithy | 36 +++++++++---------- .../generators/ServerBuilderGenerator.kt | 12 ++++++- .../protocols/parse/JsonParserGenerator.kt | 8 +++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index a5414ccc05..92bac5d018 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -14,11 +14,11 @@ service SimpleService { @http(uri: "/operation", method: "GET") operation AnOperation { - // input: RecursiveShapesInputOutput, - // output: RecursiveShapesInputOutput, - input: AnOperationInput, - output: AnOperationOutput, - /* errors: [MyError] */ + input: RecursiveShapesInputOutput, + output: RecursiveShapesInputOutput, + // input: AnOperationInput, + // output: AnOperationOutput, + // errors: [MyError] } structure AnOperationInput { @@ -106,19 +106,19 @@ string MyEnum // optInt: Integer // } -// structure RecursiveShapesInputOutput { -// nested: RecursiveShapesInputOutputNested1 -// } -// -// structure RecursiveShapesInputOutputNested1 { -// @required -// recursiveMember: RecursiveShapesInputOutputNested2 -// } -// -// structure RecursiveShapesInputOutputNested2 { -// @required -// recursiveMember: RecursiveShapesInputOutputNested1, -// } +structure RecursiveShapesInputOutput { + nested: RecursiveShapesInputOutputNested1 +} + +structure RecursiveShapesInputOutputNested1 { + @required + recursiveMember: RecursiveShapesInputOutputNested2 +} + +structure RecursiveShapesInputOutputNested2 { + @required + recursiveMember: RecursiveShapesInputOutputNested1, +} // list ValidList { // member: RecursiveShapesInputOutput diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index fda4b364d5..5fd77bb21c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -221,12 +221,22 @@ class ServerBuilderGenerator( val memberName = symbolProvider.toMemberName(member) val hasBox = symbol.mapRustType { it.stripOuter() }.isRustBoxed() + val wrapInMaybeConstrained = takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider) writer.documentShape(member, model) + if (hasBox && wrapInMaybeConstrained) { + // In the case of recursive shapes, the member might be boxed. If so, and the member is also constrained, the + // implementation of this function needs to immediately unbox the value to wrap it in `MaybeConstrained`, + // and then re-box. Clippy warns us that we could have just taken in an unboxed value to avoid this round-trip + // to the heap. However, that will make the builder take in a value whose type does not exactly match the + // shape member's type. + // We don't want to introduce API asymmetry just for this particular case, so we disable the lint. + Attribute.Custom("allow(clippy::boxed_local)").render(writer) + } writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (wrapInMaybeConstrained) { val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape || memberHasConstraintTraitOrTargetHasConstraintTrait(member) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 9dfa98baa8..46dd7530bf 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -48,6 +48,7 @@ import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.smithy.protocols.deserializeFunctionName +import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.util.PANIC import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.hasTrait @@ -241,7 +242,8 @@ class JsonParserGenerator( } private fun RustWriter.deserializeMember(memberShape: MemberShape) { - when (val target = model.expectShape(memberShape.target)) { + val target = model.expectShape(memberShape.target) + when (target) { is StringShape -> deserializeString(target) is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope) is NumberShape -> deserializeNumber(target) @@ -256,8 +258,8 @@ class JsonParserGenerator( } val symbol = symbolProvider.toSymbol(memberShape) if (symbol.isRustBoxed()) { - if (mode == CodegenMode.Server) { - // Before boxing, convert into `Validated`. + if (mode == CodegenMode.Server && memberShape.targetCanReachConstrainedShape(model, symbolProvider)) { + // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. rust(".map(|x| x.into())") } rust(".map(Box::new)") From 4fc2ee5c6f58d5d28d9a9b493a427a8c38bdc937 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 19:27:14 +0200 Subject: [PATCH 070/255] add some docs to UnconstrainedShapeSymbolProvider --- .../UnconstrainedShapeSymbolProviderTest.kt | 7 ++- .../UnconstrainedShapeSymbolProvider.kt | 63 +++++++++++++------ 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index 4740dcea50..585f253c5a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -18,9 +18,10 @@ import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup -// TODO I can't move this file to `codegen` subproject because `serverTestSymbolProvider` is only in the `codegen-server` -// subproject, but I need `UnconstrainedSymbolProvider` to be in the `codegen` subproject because it's used in the -// `JsonParserGenerator`. +/** + * While [UnconstrainedShapeSymbolProvider] _must_ be in the `codegen` subproject, these tests need to be in the + * `codegen-server` subproject, because they use [serverTestSymbolProvider]. + */ class UnconstrainedShapeSymbolProviderTest { private val baseModelString = """ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 1938250f07..947226811c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -24,6 +24,50 @@ fun unconstrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceSh return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" } +/** + * The [UnconstrainedShapeSymbolProvider] returns, _for a given constrained + * shape_, a symbol whose Rust type can hold the corresponding unconstrained + * values. + * + * For collection and map shapes, this type is a [RustType.Opaque] wrapper + * tuple newtype holding a container over the inner unconstrained type. For + * structure shapes, it's their builder type. For simple shapes, it's whatever + * the regular base symbol provider returns. + * + * So, for example, given the following model: + * + * ```smithy + * list ListA { + * member: ListB + * } + * + * list ListB { + * member: Structure + * } + * + * structure Structure { + * @required + * string: String + * } + * ``` + * + * `ListB` is not _directly_ constrained, but it is constrained, because it + * holds `Structure`s, that are constrained. So the corresponding unconstrained + * symbol has Rust type `struct + * ListBUnconstrained(std::vec::Vec)`. + * Likewise, `ListA` is also constrained. Its unconstrained symbol has Rust + * type `struct ListAUnconstrained(std::vec::Vec)`. + * + * For an _unconstrained_ shape and for simple shapes, this symbol provider + * delegates to the base symbol provider. It is therefore important that this + * symbol provider _not_ wrap [PublicConstrainedShapeSymbolProvider] (from the + * `codegen-server` subproject), because that symbol provider will return a + * constrained type for shapes that have constraint traits attached. + * + * While this symbol provider is only used by the server, it needs to be in the + * `codegen` subproject because the (common to client and server) parsers use + * it. + */ class UnconstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, @@ -55,14 +99,6 @@ class UnconstrainedShapeSymbolProvider( } else { base.toSymbol(shape) } -// check(shape.canReachConstrainedShape(model, base)) -// -// if (shape.isConstrained(base)) { -//// TODO("The `length` constraint trait on list shapes is currently not implemented") -// unconstrainedSymbolForListOrMapShape(shape) -// } else { -// unconstrainedSymbolForListOrMapShape(shape) -// } } is MapShape -> { if (shape.canReachConstrainedShape(model, base)) { @@ -70,14 +106,6 @@ class UnconstrainedShapeSymbolProvider( } else { base.toSymbol(shape) } -// check(shape.canReachConstrainedShape(model, base)) -// -// if (shape.isConstrained(base)) { -//// TODO("The `length` constraint trait on map shapes is currently not implemented") -// unconstrainedSymbolForListOrMapShape(shape) -// } else { -// unconstrainedSymbolForListOrMapShape(shape) -// } } is StructureShape -> { if (shape.canReachConstrainedShape(model, base)) { @@ -85,9 +113,6 @@ class UnconstrainedShapeSymbolProvider( } else { base.toSymbol(shape) } -// check(shape.canReachConstrainedShape(model, base)) -// -// shape.builderSymbol(base) } // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. else -> base.toSymbol(shape) From 6488239bcb38df1279a83d3e87d293e6be02ad17 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 19:35:03 +0200 Subject: [PATCH 071/255] remove unused TryFrom,TryInto imports, we're now using Rust 2021 edition --- .../smithy/generators/PublicConstrainedMapGenerator.kt | 1 - .../generators/PublicConstrainedStringGenerator.kt | 1 - .../server/smithy/generators/ServerBuilderGenerator.kt | 10 ++-------- .../smithy/generators/UnconstrainedListGenerator.kt | 5 +---- .../smithy/generators/UnconstrainedMapGenerator.kt | 1 - .../generators/UnconstrainedListGeneratorTest.kt | 6 ++---- .../smithy/generators/UnconstrainedMapGeneratorTest.kt | 2 -- 7 files changed, 5 insertions(+), 21 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt index 926f874806..a41f5c5b32 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt @@ -49,7 +49,6 @@ class PublicConstrainedMapGenerator( impl $name { pub fn parse(value: $inner) -> Result { - use std::convert::TryFrom; Self::try_from(value) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt index 03c8e283fe..c97ca834af 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -56,7 +56,6 @@ class PublicConstrainedStringGenerator( impl $name { pub fn parse(value: $inner) -> Result { - use std::convert::TryFrom; Self::try_from(value) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 5fd77bb21c..b79cc0dee6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -479,10 +479,7 @@ class ServerBuilderGenerator( """ .map(|v| match *v { #{MaybeConstrained}::Constrained(x) => Ok(Box::new(x)), - #{MaybeConstrained}::Unconstrained(x) => { - use std::convert::TryInto; - Ok(Box::new(x.try_into()?)) - } + #{MaybeConstrained}::Unconstrained(x) => Ok(Box::new(x.try_into()?)), }) .map(|res| res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } @@ -497,10 +494,7 @@ class ServerBuilderGenerator( """ .map(|v| match v { #{MaybeConstrained}::Constrained(x) => Ok(x), - #{MaybeConstrained}::Unconstrained(x) => { - use std::convert::TryInto; - x.try_into() - } + #{MaybeConstrained}::Unconstrained(x) => x.try_into(), }) .map(|res| res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt index eb43dbbc4a..0f48ea95f1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt @@ -63,10 +63,7 @@ class UnconstrainedListGenerator( let res: Result<_, #{InnerConstraintViolationSymbol}> = value .0 .into_iter() - .map(|inner| { - use std::convert::TryInto; - inner.try_into() - }) + .map(|inner| inner.try_into()) .collect(); res.map(Self) .map_err(ConstraintViolation) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 368a6b2694..77e176d9be 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -127,7 +127,6 @@ class UnconstrainedMapGenerator( let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { - use std::convert::TryInto; ${if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} ${if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(Self::Error::Value)?;" else ""} Ok((k, v)) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt index db897c11d9..3066c3d168 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt @@ -110,8 +110,7 @@ class UnconstrainedListGeneratorTest { list_a_unconstrained::ConstraintViolation(list_b_unconstrained::ConstraintViolation( crate::model::structure_c::ConstraintViolation::MissingString, )); - - use std::convert::TryFrom; + assert_eq!( expected_err, crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() @@ -132,8 +131,7 @@ class UnconstrainedListGeneratorTest { }]]; let actual: Vec> = crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); - - use std::convert::TryFrom; + assert_eq!( expected, actual diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 0fa0b78f81..f6a0767888 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -123,7 +123,6 @@ class UnconstrainedMapGeneratorTest { // crate::model::structure_c::ConstraintViolation::MissingString, // )); // -// use std::convert::TryFrom; // assert_eq!( // expected_err, // std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap_err() @@ -155,7 +154,6 @@ class UnconstrainedMapGeneratorTest { ])) ]); - use std::convert::TryFrom; assert_eq!( expected, crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() From ae351a3b7c47774921214440a8ee1218e6231001 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 21:12:22 +0200 Subject: [PATCH 072/255] add support for transitively constrained sets --- codegen-server-test/model/simple.smithy | 24 +++++++---- .../ConstraintViolationSymbolProvider.kt | 31 ++++--------- .../server/smithy/ServerCodegenVisitor.kt | 42 ++++++++++++++++-- ...rator.kt => ConstrainedCollectionShape.kt} | 7 ++- ...kt => UnconstrainedCollectionGenerator.kt} | 8 ++-- ...> UnconstrainedCollectionGeneratorTest.kt} | 11 ++--- .../smithy/ConstrainedShapeSymbolProvider.kt | 43 ++++++++----------- .../UnconstrainedShapeSymbolProvider.kt | 32 ++++++-------- 8 files changed, 104 insertions(+), 94 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{ConstrainedListGenerator.kt => ConstrainedCollectionShape.kt} (95%) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{UnconstrainedListGenerator.kt => UnconstrainedCollectionGenerator.kt} (94%) rename codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{UnconstrainedListGeneratorTest.kt => UnconstrainedCollectionGeneratorTest.kt} (96%) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 92bac5d018..e4471f2c53 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -14,10 +14,10 @@ service SimpleService { @http(uri: "/operation", method: "GET") operation AnOperation { - input: RecursiveShapesInputOutput, - output: RecursiveShapesInputOutput, - // input: AnOperationInput, - // output: AnOperationOutput, + // input: RecursiveShapesInputOutput, + // output: RecursiveShapesInputOutput, + input: AnOperationInput, + output: AnOperationOutput, // errors: [MyError] } @@ -45,13 +45,15 @@ structure ConA { // conBMap: ConBMap - normalString: NormalString, - lengthString: LengthString, - playerAction: PlayerAction, - myEnum: MyEnum + // normalString: NormalString, + // lengthString: LengthString, + // playerAction: PlayerAction, + // myEnum: MyEnum // @length(min:4, max:6) // list: LengthList, + + set: LengthStringSet } // @length(min:2, max:8) @@ -96,6 +98,12 @@ structure DirectedAction { ]) string MyEnum +// A set that is not directly constrained, but that has a member that is. There +// is no such example in any of the other test models! +set LengthStringSet { + member: LengthString +} + // structure ConB { // @required // nice: String, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 198761e721..6855017531 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -7,10 +7,9 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape @@ -25,7 +24,7 @@ import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.isConstrained import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForListOrMapShape +import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForCollectionOrMapShape import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Unit tests. @@ -36,11 +35,11 @@ class ConstraintViolationSymbolProvider( ) : WrappingSymbolProvider(base) { private val constraintViolationName = "ConstraintViolation" - private fun unconstrainedSymbolForListOrMapShape(shape: Shape): Symbol { - check(shape is ListShape || shape is MapShape) + private fun constraintViolationSymbolForCollectionOrMapShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape is MapShape) // TODO Move ConstraintViolation type to the constrained namespace. - val unconstrainedTypeName = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) + val unconstrainedTypeName = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) return Symbol.builder() @@ -53,29 +52,15 @@ class ConstraintViolationSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is SetShape -> { -// TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") - base.toSymbol(shape) - } - is ListShape -> { + is CollectionShape -> { check(shape.canReachConstrainedShape(model, base)) - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on list shapes is currently not implemented") - unconstrainedSymbolForListOrMapShape(shape) - } else { - unconstrainedSymbolForListOrMapShape(shape) - } + constraintViolationSymbolForCollectionOrMapShape(shape) } is MapShape -> { check(shape.canReachConstrainedShape(model, base)) - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on map shapes is currently not implemented") - unconstrainedSymbolForListOrMapShape(shape) - } else { - unconstrainedSymbolForListOrMapShape(shape) - } + constraintViolationSymbolForCollectionOrMapShape(shape) } is StructureShape -> { check(shape.canReachConstrainedShape(model, base)) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 86552ac7be..30503e49e6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeVisitor import software.amazon.smithy.model.shapes.StringShape @@ -19,14 +20,14 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedListGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedCollectionShape import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl -import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedListGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext @@ -244,7 +245,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") rustCrate.withModule(unconstrainedModule) { writer -> - UnconstrainedListGenerator( + UnconstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, @@ -257,7 +258,40 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for list $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedListGenerator( + ConstrainedCollectionShape( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, + writer, + shape + ).render() + } + } + } + + override fun setShape(shape: SetShape) { + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + model, + symbolProvider + ) + ) { + logger.info("[rust-server-codegen] Generating an unconstrained type for set $shape") + rustCrate.withModule(unconstrainedModule) { writer -> + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + writer, + shape + ).render() + } + + logger.info("[rust-server-codegen] Generating a constrained type for set $shape") + rustCrate.withModule(constrainedModule) { writer -> + ConstrainedCollectionShape( model, symbolProvider, unconstrainedShapeSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt similarity index 95% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt index 78e5f0c6ff..08b7040965 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility @@ -21,13 +20,13 @@ import software.amazon.smithy.rust.codegen.smithy.isConstrained // TODO Docs // TODO Unit tests -class ConstrainedListGenerator( +class ConstrainedCollectionShape( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, val writer: RustWriter, - val shape: ListShape + val shape: CollectionShape ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) @@ -59,7 +58,7 @@ class ConstrainedListGenerator( rustTemplate( """ ##[derive(Debug, Clone)] - pub(crate) struct $name(pub(crate) Vec<#{InnerConstrainedSymbol}>); + pub(crate) struct $name(pub(crate) std::vec::Vec<#{InnerConstrainedSymbol}>); impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt similarity index 94% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 0f48ea95f1..cf311129c0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility @@ -19,14 +19,14 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained // TODO Docs -class UnconstrainedListGenerator( +class UnconstrainedCollectionGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, - val shape: ListShape + val shape: CollectionShape ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) @@ -45,7 +45,7 @@ class UnconstrainedListGenerator( rustTemplate( """ ##[derive(Debug, Clone)] - pub(crate) struct $name(pub(crate) Vec<#{InnerUnconstrainedSymbol}>); + pub(crate) struct $name(pub(crate) std::vec::Vec<#{InnerUnconstrainedSymbol}>); impl From<$name> for #{MaybeConstrained} { fn from(value: $name) -> Self { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt similarity index 96% rename from codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt rename to codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 3066c3d168..3231402f5e 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedListGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.testutil.compileAndTest import software.amazon.smithy.rust.codegen.testutil.unitTest import software.amazon.smithy.rust.codegen.util.lookup -class UnconstrainedListGeneratorTest { +class UnconstrainedCollectionGeneratorTest { @Test fun `it should generate unconstrained lists`() { val model = @@ -74,7 +74,7 @@ class UnconstrainedListGeneratorTest { val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { - ConstrainedListGenerator( + ConstrainedCollectionShape( model, symbolProvider, unconstrainedShapeSymbolProvider, @@ -87,7 +87,7 @@ class UnconstrainedListGeneratorTest { project.withModule(RustModule.private("unconstrained")) { writer -> val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(listA, listB).forEach { - UnconstrainedListGenerator( + UnconstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, @@ -132,10 +132,7 @@ class UnconstrainedListGeneratorTest { let actual: Vec> = crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); - assert_eq!( - expected, - actual - ); + assert_eq!(expected, actual); """ ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt index c1e78c1ced..7242c29bc1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt @@ -8,7 +8,7 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex -import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ServiceShape @@ -21,11 +21,6 @@ import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -fun constrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceShape): String { - check(shape.isListShape || shape.isMapShape) - return "${shape.id.getName(serviceShape).toPascalCase()}Constrained" -} - class ConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, @@ -34,9 +29,9 @@ class ConstrainedShapeSymbolProvider( private val nullableIndex = NullableIndex.of(model) private fun constrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is ListShape || shape is MapShape) + check(shape is CollectionShape || shape is MapShape) - val name = constrainedTypeNameForListOrMapShape(shape, serviceShape) + val name = constrainedTypeNameForCollectionOrMapShape(shape, serviceShape) val namespace = "crate::${Constrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() @@ -68,32 +63,23 @@ class ConstrainedShapeSymbolProvider( symbol.makeRustBoxed() } else symbol + private fun errorMessage(shape: Shape) = + "This symbol provider was called with $shape. However, it can only be called with a shape that is (transitively) constrained" + override fun toSymbol(shape: Shape): Symbol = when (shape) { - is ListShape -> { - check(shape.canReachConstrainedShape(model, base)) + is CollectionShape -> { + require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on list shapes is currently not implemented") - constrainedSymbolForCollectionOrMapShape(shape) - } else { - constrainedSymbolForCollectionOrMapShape(shape) - } + constrainedSymbolForCollectionOrMapShape(shape) } is MapShape -> { - check(shape.canReachConstrainedShape(model, base)) + require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } - if (shape.isConstrained(base)) { -// TODO("The `length` constraint trait on map shapes is currently not implemented") - constrainedSymbolForCollectionOrMapShape(shape) - } else { - constrainedSymbolForCollectionOrMapShape(shape) - } + constrainedSymbolForCollectionOrMapShape(shape) } is MemberShape -> { - check(shape.canReachConstrainedShape(model, base)) { - shape.id - } + require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } check(model.expectShape(shape.container).isStructureShape) if (shape.requiresNewtype()) { @@ -133,3 +119,8 @@ class ConstrainedShapeSymbolProvider( } } } + +fun constrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { + check(shape is CollectionShape || shape is MapShape) + return "${shape.id.getName(serviceShape).toPascalCase()}Constrained" +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 947226811c..96ad328c8a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -7,10 +7,9 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords @@ -19,11 +18,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -fun unconstrainedTypeNameForListOrMapShape(shape: Shape, serviceShape: ServiceShape): String { - check(shape.isListShape || shape.isMapShape) - return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" -} - /** * The [UnconstrainedShapeSymbolProvider] returns, _for a given constrained * shape_, a symbol whose Rust type can hold the corresponding unconstrained @@ -73,10 +67,11 @@ class UnconstrainedShapeSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { - private fun unconstrainedSymbolForListOrMapShape(shape: Shape): Symbol { - check(shape is ListShape || shape is MapShape) - val name = unconstrainedTypeNameForListOrMapShape(shape, serviceShape) + private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape is MapShape) + + val name = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() @@ -89,20 +84,16 @@ class UnconstrainedShapeSymbolProvider( override fun toSymbol(shape: Shape): Symbol = when (shape) { - is SetShape -> { -// TODO("Set shapes can only contain some simple shapes, but constraint traits on simple shapes are not implemented") - base.toSymbol(shape) - } - is ListShape -> { + is CollectionShape -> { if (shape.canReachConstrainedShape(model, base)) { - unconstrainedSymbolForListOrMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } else { base.toSymbol(shape) } } is MapShape -> { if (shape.canReachConstrainedShape(model, base)) { - unconstrainedSymbolForListOrMapShape(shape) + unconstrainedSymbolForCollectionOrMapShape(shape) } else { base.toSymbol(shape) } @@ -114,7 +105,12 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } } - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Simple shapes can have constraint traits. + // TODO Arm for `MemberShape`s. else -> base.toSymbol(shape) } } + +fun unconstrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { + check(shape is CollectionShape || shape is MapShape) + return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" +} From ca32c8ec4fde8f2d556b7cc1830c9735fb7e7e93 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 21:45:20 +0200 Subject: [PATCH 073/255] rename isConstrained to isDirectlyConstrained --- .../ConstraintViolationSymbolProvider.kt | 4 +- .../PublicConstrainedShapeSymbolProvider.kt | 8 +- .../server/smithy/ServerCodegenVisitor.kt | 10 +- .../generators/ConstrainedCollectionShape.kt | 4 +- .../generators/ConstrainedMapGenerator.kt | 6 +- .../generators/ServerBuilderGenerator.kt | 4 +- .../generators/UnconstrainedMapGenerator.kt | 14 +- .../codegen/server/smithy/ConstraintsTest.kt | 148 +++++++++--------- .../smithy/rust/codegen/smithy/Constraints.kt | 61 +++----- 9 files changed, 125 insertions(+), 134 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 6855017531..e39e5aba43 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -22,7 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForCollectionOrMapShape import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -77,7 +77,7 @@ class ConstraintViolationSymbolProvider( .build() } is StringShape -> { - check(shape.isConstrained(base)) + check(shape.isDirectlyConstrained(base)) val namespace = "crate::${Models.namespace}::${RustReservedWords.escapeIfNeeded(shape.contextName(serviceShape).toSnakeCase())}" val rustType = RustType.Opaque(constraintViolationName, namespace) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt index 0fc4fa42e1..307c99a06a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt @@ -22,7 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.contextName -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.locatedIn import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed @@ -57,7 +57,7 @@ class PublicConstrainedShapeSymbolProvider( handleOptionality(handleRustBoxing(targetSymbol, shape), shape) } is MapShape -> { - if (shape.isConstrained(base)) { + if (shape.isDirectlyConstrained(base)) { check(shape.hasTrait()) { "Only the `length` constraint trait can be applied to maps" } publicConstrainedSymbolForCollectionOrMapShape(shape) } else { @@ -71,7 +71,7 @@ class PublicConstrainedShapeSymbolProvider( } is CollectionShape -> { // TODO Both arms return the same because we haven't implemented any constraint trait on collection shapes yet. - if (shape.isConstrained(base)) { + if (shape.isDirectlyConstrained(base)) { val inner = this.toSymbol(shape.member) symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() } else { @@ -80,7 +80,7 @@ class PublicConstrainedShapeSymbolProvider( } } is StringShape -> { - if (!shape.isConstrained(base)) { + if (!shape.isDirectlyConstrained(base)) { return base.toSymbol(shape) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 30503e49e6..73d6c1475f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -50,7 +50,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.smithy.transformers.AddErrorMessage @@ -304,7 +304,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun mapShape(shape: MapShape) { - if (shape.isConstrained(symbolProvider)) { + if (shape.isDirectlyConstrained(symbolProvider)) { rustCrate.useShapeWriter(shape) { writer -> PublicConstrainedMapGenerator( model, @@ -335,7 +335,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ).render() } - if (!shape.isConstrained(symbolProvider)) { + if (!shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") rustCrate.withModule(constrainedModule) { writer -> ConstrainedMapGenerator( @@ -364,7 +364,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } - if (shape.hasTrait() && shape.isConstrained(symbolProvider)) { + if (shape.hasTrait() && shape.isDirectlyConstrained(symbolProvider)) { logger.warning( """ String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy @@ -373,7 +373,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: See https://github.com/awslabs/smithy/issues/1121f for more information. """.trimIndent() ) - } else if (shape.isConstrained(symbolProvider)) { + } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> PublicConstrainedStringGenerator( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt index 08b7040965..10e949a748 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained // TODO Docs // TODO Unit tests @@ -52,7 +52,7 @@ class ConstrainedCollectionShape( // // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. val targetNeedsConstraining = - !innerShape.isConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape.isMapShape) + !innerShape.isDirectlyConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape.isMapShape) writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index fe0a9ba408..10590e1d52 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -20,7 +20,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained // TODO Docs // TODO Unit tests @@ -88,13 +88,13 @@ class ConstrainedMapGenerator( } // TODO These are copied from `UnconstrainedMapGenerator.kt`. - private fun isKeyConstrained(shape: StringShape) = shape.isConstrained(symbolProvider) + private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) private fun isValueConstrained(shape: Shape): Boolean = when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - is StringShape -> shape.isConstrained(symbolProvider) + is StringShape -> shape.isDirectlyConstrained(symbolProvider) // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. else -> false } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index b79cc0dee6..734d8f7208 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,7 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -410,7 +410,7 @@ class ServerBuilderGenerator( } private fun memberHasConstraintTraitOrTargetHasConstraintTrait(member: MemberShape) = - member.isConstrained(symbolProvider) || (model.expectShape(member.target).isConstrained(symbolProvider)) + member.isDirectlyConstrained(symbolProvider) || (model.expectShape(member.target).isDirectlyConstrained(symbolProvider)) /** * Returns the symbol for a builder's member. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 77e176d9be..badbc7095d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -23,7 +23,7 @@ import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait @@ -42,7 +42,7 @@ class UnconstrainedMapGenerator( private val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) private val valueShape = model.expectShape(shape.value.target) - private val constrainedSymbol = if (shape.isConstrained(symbolProvider)) { + private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { symbolProvider.toSymbol(shape) } else { constrainedShapeSymbolProvider.toSymbol(shape) @@ -114,7 +114,7 @@ class UnconstrainedMapGenerator( if (isKeyConstrained(keyShape) || isValueConstrained(valueShape)) { val resolveToNonPublicConstrainedValueType = isValueConstrained(valueShape) && - !valueShape.isConstrained(symbolProvider) && + !valueShape.isDirectlyConstrained(symbolProvider) && !valueShape.isStructureShape val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { constrainedShapeSymbolProvider.toSymbol(valueShape) @@ -139,7 +139,7 @@ class UnconstrainedMapGenerator( ) val constrainedValueTypeIsNotFinalType = - resolveToNonPublicConstrainedValueType && shape.isConstrained(symbolProvider) + resolveToNonPublicConstrainedValueType && shape.isDirectlyConstrained(symbolProvider) if (constrainedValueTypeIsNotFinalType) { // The map is constrained. Its value shape reaches a constrained shape, but the value shape itself // is not directly constrained. The value shape must be an aggregate shape. But it is not a @@ -178,7 +178,7 @@ class UnconstrainedMapGenerator( rust("let hm = value.0;") } - if (shape.isConstrained(symbolProvider)) { + if (shape.isDirectlyConstrained(symbolProvider)) { rust("Self::try_from(hm)") } else { rust("Ok(Self(hm))") @@ -187,13 +187,13 @@ class UnconstrainedMapGenerator( } } - private fun isKeyConstrained(shape: StringShape) = shape.isConstrained(symbolProvider) + private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) private fun isValueConstrained(shape: Shape): Boolean = when (shape) { is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - is StringShape -> shape.isConstrained(symbolProvider) + is StringShape -> shape.isDirectlyConstrained(symbolProvider) // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. else -> false } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index 870d40e06f..fcfe28ca06 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -14,8 +14,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.hasConstraintTrait -import software.amazon.smithy.rust.codegen.smithy.isConstrained +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.requiresNewtype import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup @@ -23,70 +22,73 @@ import software.amazon.smithy.rust.codegen.util.lookup class ConstraintsTest { private val model = """ - namespace test + namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - map: MapA, - - recursive: RecursiveShape - } - - structure RecursiveShape { - shape: RecursiveShape, - mapB: MapB - } - - @length(min: 1, max: 69) - map MapA { - key: String, - value: MapB - } - - map MapB { - key: String, - value: StructureA - } + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + map: MapA, - @uniqueItems - list ListA { - member: MyString - } + recursive: RecursiveShape + } + + structure RecursiveShape { + shape: RecursiveShape, + mapB: MapB + } + + @length(min: 1, max: 69) + map MapA { + key: String, + value: MapB + } + + map MapB { + key: String, + value: StructureA + } + + @uniqueItems + list ListA { + member: MyString + } + + @pattern("\\w+") + string MyString + + @length(min: 1, max: 69) + string LengthString + + structure StructureA { + @range(min: 1, max: 69) + int: Integer, + @required + string: String + } + + // This shape is not in the service closure. + structure StructureB { @pattern("\\w+") - string MyString + patternString: String, + + @required + requiredString: String, - structure StructureA { - @range(min: 1, max: 69) - int: Integer, - - @required - string: String - } + mapA: MapA, - // This shape is not in the service closure. - structure StructureB { - @pattern("\\w+") - patternString: String, - - @required - requiredString: String, - - mapA: MapA, - - @length(min: 1, max: 5) - mapAPrecedence: MapA - } - """.asSmithyModel() + @length(min: 1, max: 5) + mapAPrecedence: MapA + } + """.asSmithyModel() private val symbolProvider = serverTestSymbolProvider(model) private val testInputOutput = model.lookup("test#TestInputOutput") @@ -95,35 +97,33 @@ class ConstraintsTest { private val mapB = model.lookup("test#MapB") private val listA = model.lookup("test#ListA") private val myString = model.lookup("test#MyString") + private val lengthString = model.lookup("test#LengthString") private val structA = model.lookup("test#StructureA") private val structAInt = model.lookup("test#StructureA\$int") private val structAString = model.lookup("test#StructureA\$string") - @Test - fun `it should recognize constraint traits`() { - listOf(mapA, structAInt, structAString, myString).forEach { - it.hasConstraintTrait() shouldBe true - } - - listOf(mapB, structA).forEach { - it.hasConstraintTrait() shouldBe false - } - } - @Test fun `it should not recognize uniqueItems as a constraint trait because it's deprecated`() { - listA.hasConstraintTrait() shouldBe false + listA.isDirectlyConstrained(symbolProvider) shouldBe false } @Test fun `it should detect supported constrained traits as constrained`() { - structA.isConstrained(symbolProvider) shouldBe true + listOf(mapA, structA, lengthString).forEach { + // TODO When a test like this one fails, we get: + // ``` + // io.kotest.assertions.AssertionFailedError: expected: but was: + // at software.amazon.smithy.rust.codegen.server.smithy.ConstraintsTest.it should detect supported constrained traits as constrained(ConstraintsTest.kt:109) + // ``` + // How can I make it so that it prints `it` to determine which particular case failed? + it.isDirectlyConstrained(symbolProvider) shouldBe true + } } @Test fun `it should not detect unsupported constrained traits as constrained`() { listOf(structAInt, structAString, myString).forEach { - it.isConstrained(symbolProvider) shouldBe false + it.isDirectlyConstrained(symbolProvider) shouldBe false } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 07570ac86f..851840128c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -15,64 +15,55 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait -import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.util.hasTrait /** - * A shape has a constraint trait if it has one of these traits attached. - */ -fun Shape.hasConstraintTrait() = - this.hasTrait() || - this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() - -// TODO Maybe we should rename this to `isDirectlyConstrained`. -// TODO Perhaps it's best that we specialize and have `StringShape.isConstrained()`, `MapShape.isConstrained()` etc, -// and we check only for the supported constraint traits and also only check for the ones compatible according to the -// Smithy spec selectors. -/** - * A shape is constrained if: + * We say a shape is _directly_ constrained if: * * - it has a constraint trait, or; * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. * * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is - * a constrained shape is _not_ constrained. + * a constrained shape is _not_ directly constrained. * - * At the moment the only supported constraint trait is `required`, which can only be attached to structure member shapes. + * At the moment only a subset of constraint traits are implemented on a subset of shapes; that's why we match against + * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's + * (with these subsets being smaller than what [the spec] accounts for). + * + * Note `uniqueItems` is deprecated, so we won't ever implement it. + * + * [the spec]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html */ -fun Shape.isConstrained(symbolProvider: SymbolProvider) = when (this) { +fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The only reason why the functions in this file have - // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as - // `required`, so we can't use `member.isOptional` here. + // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } - is MapShape -> this.hasConstraintTrait() - is StringShape -> !this.hasTrait() && this.hasTrait() // TODO For the moment only `length` on string shapes is supported. - else -> { - // this.hasConstraintTrait() - false - } + is MapShape -> this.hasTrait() + // TODO While `enum` traits are constraint traits, we're outright rejecting unknown enum variants as deserialization + // errors instead of parsing them into an unconstrained type that we then constrain after parsing the entire request. + is StringShape -> !this.hasTrait() && this.hasTrait() + else -> false } fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } -fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) -fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) +fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) +fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isConstrained(symbolProvider) } + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - this.isConstrained(symbolProvider) || this.targetCanReachConstrainedShape(model, symbolProvider) + this.isDirectlyConstrained(symbolProvider) || this.targetCanReachConstrainedShape(model, symbolProvider) // TODO Callers should use `MemberShape.canReachConstrainedShape`, and this function should be inlined. fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = @@ -80,7 +71,7 @@ fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: Sym is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) - else -> targetShape.isConstrained(symbolProvider) + else -> targetShape.isDirectlyConstrained(symbolProvider) } fun MemberShape.requiresNewtype() = From 07007abcdd51e9f90340c83ecf2790703c3c2c71 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 13 May 2022 23:13:44 +0200 Subject: [PATCH 074/255] only map holding constrained value needs iterating to constrain value --- codegen-server-test/model/simple.smithy | 10 +++++++- .../generators/ConstrainedCollectionShape.kt | 6 ++--- .../generators/ConstrainedMapGenerator.kt | 25 +++++++++++++------ .../UnconstrainedShapeSymbolProvider.kt | 3 +++ .../generators/http/HttpBindingGenerator.kt | 2 +- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index e4471f2c53..ec18d4d8e0 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -53,7 +53,15 @@ structure ConA { // @length(min:4, max:6) // list: LengthList, - set: LengthStringSet + //set: LengthStringSet, + + @httpPrefixHeaders("X-Foo-") + headerMap: HeaderMap +} + +map HeaderMap { + key: LengthString, + value: LengthString } // @length(min:2, max:8) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt index 10e949a748..933dc9efad 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt @@ -51,7 +51,7 @@ class ConstrainedCollectionShape( // generated code a little bit simpler. // // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. - val targetNeedsConstraining = + val innerNeedsConstraining = !innerShape.isDirectlyConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape.isMapShape) writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { @@ -66,7 +66,7 @@ class ConstrainedCollectionShape( impl From<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { - ${ if (targetNeedsConstraining) { + ${ if (innerNeedsConstraining) { "Self(v.into_iter().map(|item| item.into()).collect())" } else { "Self(v)" @@ -76,7 +76,7 @@ class ConstrainedCollectionShape( impl From<$name> for #{Symbol} { fn from(v: $name) -> Self { - ${ if (targetNeedsConstraining) { + ${ if (innerNeedsConstraining) { "v.0.into_iter().map(|item| item.into()).collect()" } else { "v.0" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 10590e1d52..5a68d44eee 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -53,9 +53,12 @@ class ConstrainedMapGenerator( symbolProvider.toSymbol(valueShape) } - // The converters are only needed when the constrained type is `pub(crate)`, for the server builder function - // member function to work. - // Note that unless the map holds an aggregate shape as its value shape, the `.into()` calls are useless. + // Unless the map holds an aggregate shape as its value shape whose symbol's type is _not_ `pub(crate)`, the + // `.into()` calls are useless. + // See the comment in [ConstrainedCollectionShape] for a more detailed explanation. + val innerNeedsConstraining = + !valueShape.isDirectlyConstrained(symbolProvider) && (valueShape is CollectionShape || valueShape.isMapShape) + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -67,14 +70,22 @@ class ConstrainedMapGenerator( } impl From<#{Symbol}> for $name { - fn from(hm: #{Symbol}) -> Self { - Self(hm.into_iter().map(|(k, v)| (k, v.into())).collect()) + fn from(v: #{Symbol}) -> Self { + ${ if (innerNeedsConstraining) { + "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" + } else { + "Self(v)" + } } } } impl From<$name> for #{Symbol} { - fn from(wrapper: $name) -> Self { - wrapper.0.into_iter().map(|(k, v)| (k, v.into())).collect() + fn from(v: $name) -> Self { + ${ if (innerNeedsConstraining) { + "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" + } else { + "v.0" + } } } } """, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 96ad328c8a..d73d602bb6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -105,6 +105,9 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } } +// is MemberShape -> { +// TODO("Constraint traits on member shape not implemented yet") +// } // TODO Arm for `MemberShape`s. else -> base.toSymbol(shape) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 1f10f953dd..f5a8bbcd1b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -62,7 +62,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase * - serializing data to an HTTP request (we are a client), * - serializing data to an HTTP response (we are a server), */ -public enum class HttpMessageType { +enum class HttpMessageType { REQUEST, RESPONSE } From 5c7636db2451a24b1aec7fd4f848403dff1ce612 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 17 May 2022 19:00:02 +0200 Subject: [PATCH 075/255] support maps of constrained strings with httpHeaderPrefix trait --- codegen-server-test/model/simple.smithy | 35 ++++++++++++--- .../UnconstrainedShapeSymbolProvider.kt | 43 +++++++++++++++++-- .../generators/http/HttpBindingGenerator.kt | 22 +++++++--- rust-runtime/aws-smithy-http/src/header.rs | 2 + 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index ec18d4d8e0..c8cfd3620c 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -23,7 +23,29 @@ operation AnOperation { structure AnOperationInput { @required - conA: ConA + conA: ConA, + + // Only top-level members of an operation's input structure are considered + // when deserializing HTTP messages. + + @httpHeader("X-Length") + lengthString: LengthString, + + @httpHeader("X-Length-Set") + lengthStringSet: LengthStringSet, + + @httpHeader("X-Length-List") + lengthStringList: LengthStringList, + + @httpHeader("X-String") + string: String, + + // @required + @httpPrefixHeaders("X-Required-") + requiredHeaderMap: HeaderMap, + + //@httpPrefixHeaders("X-Foo-") + //headerMap: HeaderMap } structure AnOperationOutput { @@ -46,7 +68,7 @@ structure ConA { // conBMap: ConBMap // normalString: NormalString, - // lengthString: LengthString, + // playerAction: PlayerAction, // myEnum: MyEnum @@ -54,14 +76,11 @@ structure ConA { // list: LengthList, //set: LengthStringSet, - - @httpPrefixHeaders("X-Foo-") - headerMap: HeaderMap } map HeaderMap { key: LengthString, - value: LengthString + value: LengthString, } // @length(min:2, max:8) @@ -112,6 +131,10 @@ set LengthStringSet { member: LengthString } +list LengthStringList { + member: LengthString +} + // structure ConB { // @required // nice: String, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index d73d602bb6..6b86d62f5b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -7,14 +7,17 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -67,6 +70,7 @@ class UnconstrainedShapeSymbolProvider( private val model: Model, private val serviceShape: ServiceShape, ) : WrappingSymbolProvider(base) { + private val nullableIndex = NullableIndex.of(model) private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { check(shape is CollectionShape || shape is MapShape) @@ -82,6 +86,27 @@ class UnconstrainedShapeSymbolProvider( .build() } + // TODO The following two methods have been copied from `SymbolVisitor.kt`. + private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = + if (member.isRequired) { + symbol + } else if (nullableIndex.isNullable(member)) { + symbol.makeOptional() + } else { + symbol + } + + /** + * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with + * [RustBoxTrait]; otherwise returns [symbol] unchanged. + * + * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. + */ + private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = + if (shape.hasTrait()) { + symbol.makeRustBoxed() + } else symbol + override fun toSymbol(shape: Shape): Symbol = when (shape) { is CollectionShape -> { @@ -105,10 +130,20 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } } -// is MemberShape -> { -// TODO("Constraint traits on member shape not implemented yet") -// } - // TODO Arm for `MemberShape`s. + is MemberShape -> { + // The only case where we use this symbol provider on a member shape is when generating deserializers + // for HTTP-bound member shapes. See how e.g. [HttpBindingGenerator] generates deserializers for a member + // shape with the `httpPrefixHeaders` trait targeting a map shape of string keys and values. + if (model.expectShape(shape.container).isStructureShape && shape.targetCanReachConstrainedShape(model, base)) { + val targetShape = model.expectShape(shape.target) + val targetSymbol = this.toSymbol(targetShape) + // Handle boxing first so we end up with `Option>`, not `Box>`. + handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + } else { + base.toSymbol(shape) + } + // TODO Constraint traits on member shapes are not implemented yet. + } else -> base.toSymbol(shape) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index f5a8bbcd1b..2c3ae04f5a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -35,16 +35,19 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.generators.redactIfNecessary import software.amazon.smithy.rust.codegen.smithy.makeOptional +import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.util.UNREACHABLE import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.hasTrait @@ -131,8 +134,11 @@ class HttpBindingGenerator( fun generateDeserializePrefixHeaderFn(binding: HttpBindingDescriptor): RuntimeType { check(binding.location == HttpBinding.Location.PREFIX_HEADERS) + val returnUnconstrainedType = mode == CodegenMode.Server && binding.member.targetCanReachConstrainedShape(model, symbolProvider) val outputT = symbolProvider.toSymbol(binding.member) - check(outputT.rustType().stripOuter() is RustType.HashMap) { outputT.rustType() } + if (!returnUnconstrainedType) { + check(outputT.rustType().stripOuter() is RustType.HashMap) { outputT.rustType() } + } val target = model.expectShape(binding.member.target) check(target is MapShape) val fnName = "deser_prefix_header_${fnName(operationShape, binding)}" @@ -148,7 +154,7 @@ class HttpBindingGenerator( } return RuntimeType.forInlineFun(fnName, httpSerdeModule) { writer -> writer.rustBlock( - "pub fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", + "pub(crate) fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", RuntimeType.http, outputT, headerUtil @@ -159,13 +165,19 @@ class HttpBindingGenerator( let out: std::result::Result<_, _> = headers.map(|(key, header_name)| { let values = header_map.get_all(header_name); #T(values.iter()).map(|v| (key.to_string(), v.expect( - "we have checked there is at least one value for this header name; please file a bug report under https://github.com/awslabs/smithy-rs/issues - "))) + "we have checked there is at least one value for this header name; please file a bug report under https://github.com/awslabs/smithy-rs/issues" + ))) }).collect(); - out.map(Some) """, headerUtil, inner ) + + if (returnUnconstrainedType) { + // If the map shape has constrained string keys or values, we need to wrap the deserialized hash map + // in the corresponding unconstrained wrapper tuple struct. + rust("let out = out.map(|hm| #T(hm));", outputT.mapRustType { it.stripOuter() }) + } + rust("out.map(Some)") } } } diff --git a/rust-runtime/aws-smithy-http/src/header.rs b/rust-runtime/aws-smithy-http/src/header.rs index 40a1daaf5e..a70c360713 100644 --- a/rust-runtime/aws-smithy-http/src/header.rs +++ b/rust-runtime/aws-smithy-http/src/header.rs @@ -73,6 +73,8 @@ pub fn many_dates( Ok(out) } +/// Returns an iterator over pairs where the first element is the unprefixed header name that +/// starts with the input `key` prefix, and the second element is the full header name. pub fn headers_for_prefix<'a>( headers: &'a http::HeaderMap, key: &'a str, From 5accf0b7501359cf41f5f707966ceda4056ae584 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 13:25:18 +0200 Subject: [PATCH 076/255] support lists/sets of constrained strings with httpHeader trait --- codegen-server-test/model/simple.smithy | 1 + .../generators/http/HttpBindingGenerator.kt | 58 +++++++++++++------ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index c8cfd3620c..66fae58042 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -28,6 +28,7 @@ structure AnOperationInput { // Only top-level members of an operation's input structure are considered // when deserializing HTTP messages. + // TODO Test with mediaType trait too. @httpHeader("X-Length") lengthString: LengthString, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 2c3ae04f5a..ac2bc394ec 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -38,6 +38,7 @@ import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.generators.redactIfNecessary import software.amazon.smithy.rust.codegen.smithy.makeOptional @@ -121,7 +122,7 @@ class HttpBindingGenerator( val fnName = "deser_header_${fnName(operationShape, binding)}" return RuntimeType.forInlineFun(fnName, httpSerdeModule) { writer -> writer.rustBlock( - "pub fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", + "pub(crate) fn $fnName(header_map: &#T::HeaderMap) -> std::result::Result<#T, #T::ParseError>", RuntimeType.http, outputT, headerUtil @@ -314,19 +315,20 @@ class HttpBindingGenerator( * Parse a value from a header. * This function produces an expression which produces the precise type required by the target shape. */ - private fun RustWriter.deserializeFromHeader(targetType: Shape, memberShape: MemberShape) { - val rustType = symbolProvider.toSymbol(targetType).rustType().stripOuter() + private fun RustWriter.deserializeFromHeader(targetShape: Shape, memberShape: MemberShape) { + val rustType = symbolProvider.toSymbol(targetShape).rustType().stripOuter() // Normally, we go through a flow that looks for `,`s but that's wrong if the output // is just a single string (which might include `,`s.). // MediaType doesn't include `,` since it's base64, send that through the normal path - if (targetType is StringShape && !targetType.hasTrait()) { + if (targetShape is StringShape && !targetShape.hasTrait()) { rust("#T::one_or_none(headers)", headerUtil) return } - val (coreType, coreShape) = if (targetType is CollectionShape) { - rustType.stripOuter() to model.expectShape(targetType.member.target) + val (coreType, coreShape) = if (targetShape is CollectionShape) { + val coreShape = model.expectShape(targetShape.member.target) + symbolProvider.toSymbol(coreShape).rustType() to coreShape } else { - rustType to targetType + rustType to targetShape } val parsedValue = safeName() if (coreType == dateTime) { @@ -338,18 +340,22 @@ class HttpBindingGenerator( ) val timestampFormatType = RuntimeType.TimestampFormat(runtimeConfig, timestampFormat) rust( - "let $parsedValue: Vec<${coreType.render(true)}> = #T::many_dates(headers, #T)?;", + "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?;", headerUtil, timestampFormatType ) } else if (coreShape.isPrimitive()) { rust( - "let $parsedValue = #T::read_many_primitive::<${coreType.render(fullyQualified = true)}>(headers)?;", + "let $parsedValue = #T::read_many_primitive::<${coreType.render()}>(headers)?;", headerUtil ) } else { + check(coreShape.isStringShape) { + "The `httpHeader` trait can be applied to structure members that target a `boolean`, `number`, `string`, or " + + "`timestamp`; or a `structure` member that targets a list/set of these types. Found $coreShape." + } rust( - "let $parsedValue: Vec<${coreType.render(fullyQualified = true)}> = #T::read_many_from_str(headers)?;", + "let $parsedValue: Vec<${coreType.render()}> = #T::read_many_from_str(headers)?;", headerUtil ) if (coreShape.hasTrait()) { @@ -389,17 +395,31 @@ class HttpBindingGenerator( }) """ ) - else -> rustTemplate( - """ - if $parsedValue.len() > 1 { - Err(#{header_util}::ParseError::new_with_message(format!("expected one item but found {}", $parsedValue.len()))) + else -> { + val returnUnconstrainedType = mode == CodegenMode.Server && + targetShape is CollectionShape && + targetShape.canReachConstrainedShape(model, symbolProvider) + if (returnUnconstrainedType) { + rust( + """ + Ok(Some(#T($parsedValue))) + """, + symbolProvider.toSymbol(targetShape) + ) } else { - let mut $parsedValue = $parsedValue; - Ok($parsedValue.pop()) + rustTemplate( + """ + if $parsedValue.len() > 1 { + Err(#{header_util}::ParseError::new_with_message(format!("expected one item but found {}", $parsedValue.len()))) + } else { + let mut $parsedValue = $parsedValue; + Ok($parsedValue.pop()) + } + """, + "header_util" to headerUtil + ) } - """, - "header_util" to headerUtil - ) + } } } From 9b163ab86fdd2419928d2c849519c20e3c147ace Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 15:06:22 +0200 Subject: [PATCH 077/255] support lists/sets of constrained strings with httpQuery trait --- codegen-server-test/model/simple.smithy | 32 ++++++++++-------- .../ServerHttpBoundProtocolGenerator.kt | 33 ++++++++++++------- .../UnconstrainedShapeSymbolProvider.kt | 7 ++-- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 66fae58042..a520b593ab 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -29,24 +29,28 @@ structure AnOperationInput { // when deserializing HTTP messages. // TODO Test with mediaType trait too. - @httpHeader("X-Length") - lengthString: LengthString, + // @httpHeader("X-Length") + // lengthStringHeader: LengthString, - @httpHeader("X-Length-Set") - lengthStringSet: LengthStringSet, + // @httpHeader("X-Length-Set") + // lengthStringSetHeader: LengthStringSet, - @httpHeader("X-Length-List") - lengthStringList: LengthStringList, + // @httpHeader("X-Length-List") + // lengthStringListHeader: LengthStringList, - @httpHeader("X-String") - string: String, + // // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working + // // @required + // @httpPrefixHeaders("X-Prefix-Headers-") + // lengthStringHeaderMap: LengthStringHeaderMap, - // @required - @httpPrefixHeaders("X-Required-") - requiredHeaderMap: HeaderMap, + @httpQuery("lengthString") + lengthStringQuery: LengthString, + + @httpQuery("lengthStringList") + lengthStringListQuery: LengthStringList, - //@httpPrefixHeaders("X-Foo-") - //headerMap: HeaderMap + @httpQuery("lengthStringSet") + lengthStringSetQuery: LengthStringSet, } structure AnOperationOutput { @@ -79,7 +83,7 @@ structure ConA { //set: LengthStringSet, } -map HeaderMap { +map LengthStringHeaderMap { key: LengthString, value: LengthString, } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 2e2d399b00..fbf682f99d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -26,11 +26,13 @@ import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asType +import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable @@ -40,6 +42,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerR import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName @@ -49,6 +52,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.protocol.MakeOperat import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.smithy.isOptional +import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation @@ -958,20 +962,25 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) } queryBindingsTargettingCollection.forEach { + // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. + val hasConstrainedTarget = + model.expectShape(it.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) + val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider val memberName = symbolProvider.toMemberName(it.member) - rustTemplate( - """ - if !$memberName.is_empty() { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(${ - if (symbolProvider.toSymbol(it.member).isOptional()) { - "Some($memberName)" - } else { - memberName - } - }); + val isOptional = symbolProvider.toSymbol(it.member).isOptional() + rustBlock("if !$memberName.is_empty()") { + withBlock("input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(", ");") { + conditionalBlock("Some(", ")", conditional = isOptional) { + conditionalBlock( + "#T(", + ")", + conditional = hasConstrainedTarget, + symbolProvider.toSymbol(it.member).mapRustType { t -> t.stripOuter() }) { + write(memberName) + } + } } - """.trimIndent() - ) + } } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 6b86d62f5b..dc0f8e9d40 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -132,8 +132,11 @@ class UnconstrainedShapeSymbolProvider( } is MemberShape -> { // The only case where we use this symbol provider on a member shape is when generating deserializers - // for HTTP-bound member shapes. See how e.g. [HttpBindingGenerator] generates deserializers for a member - // shape with the `httpPrefixHeaders` trait targeting a map shape of string keys and values. + // for HTTP-bound member shapes. See, for example: + // * how [HttpBindingGenerator] generates deserializers for a member shape with the `httpPrefixHeaders` + // trait targeting a map shape of string keys and values; or + // * how [ServerHttpBoundProtocolGenerator] deserializes for a member shape with the `httpQuery` trait + // targeting a collection shape that can reach a constrained shape. if (model.expectShape(shape.container).isStructureShape && shape.targetCanReachConstrainedShape(model, base)) { val targetShape = model.expectShape(shape.target) val targetSymbol = this.toSymbol(targetShape) From 5acda336efcdc6a190e1e18fb3e5763edb110eb0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 15:34:48 +0200 Subject: [PATCH 078/255] support maps of constrained strings with httpQueryParams trait --- codegen-server-test/model/simple.smithy | 55 ++++++++++++++++- .../ServerHttpBoundProtocolGenerator.kt | 59 ++++++++++++------- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index a520b593ab..fdc3ba69fa 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -8,7 +8,10 @@ use aws.protocols#restJson1 @title("SimpleService") service SimpleService { operations: [ - AnOperation, + //AnOperation, + QueryParamsTargetingMapOfLengthString, + // QueryParamsTargetingMapOfListOfLengthString, + // QueryParamsTargetingMapOfSetOfLengthString, ], } @@ -21,6 +24,39 @@ operation AnOperation { // errors: [MyError] } +@http(uri: "/query-params-targeting-map-of-length-string", method: "GET") +operation QueryParamsTargetingMapOfLengthString { + input: QueryParamsTargetingMapOfLengthStringInputOutput, + output: QueryParamsTargetingMapOfLengthStringInputOutput, +} + +@http(uri: "/query-params-targeting-map-of-list-of-length-string", method: "GET") +operation QueryParamsTargetingMapOfListOfLengthString { + input: QueryParamsTargetingMapOfListOfLengthStringInputOutput, + output: QueryParamsTargetingMapOfListOfLengthStringInputOutput, +} + +@http(uri: "/query-params-targeting-map-of-set-of-length-string", method: "GET") +operation QueryParamsTargetingMapOfSetOfLengthString { + input: QueryParamsTargetingMapOfSetOfLengthStringInputOutput, + output: QueryParamsTargetingMapOfSetOfLengthStringInputOutput, +} + +structure QueryParamsTargetingMapOfLengthStringInputOutput { + @httpQueryParams + mapOfLengthString: MapOfLengthString +} + +structure QueryParamsTargetingMapOfListOfLengthStringInputOutput { + @httpQueryParams + mapOfListOfLengthString: MapOfListOfLengthString +} + +structure QueryParamsTargetingMapOfSetOfLengthStringInputOutput { + @httpQueryParams + mapOfSetOfLengthString: MapOfSetOfLengthString +} + structure AnOperationInput { @required conA: ConA, @@ -83,11 +119,27 @@ structure ConA { //set: LengthStringSet, } +// TODO Use MapOfLengthString map LengthStringHeaderMap { key: LengthString, value: LengthString, } +map MapOfLengthString { + key: LengthString, + value: LengthString, +} + +map MapOfListOfLengthString { + key: LengthString, + value: LengthStringList, +} + +map MapOfSetOfLengthString { + key: LengthString, + value: LengthStringSet, +} + // @length(min:2, max:8) // list LengthList { // member: LengthString @@ -130,6 +182,7 @@ structure DirectedAction { ]) string MyEnum +// TODO Rename these to ListOfLengthString, SetOfLengthString // A set that is not directly constrained, but that has a member that is. There // is no such example in any of the other test models! set LengthStringSet { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index fbf682f99d..0bad998359 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.node.ExpectationNotMetException import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape @@ -935,47 +936,63 @@ private class ServerHttpBoundProtocolTraitImplGenerator( when (queryParamsBinding.queryParamsBindingTargetMapValueType()) { QueryParamsTargetMapValueType.STRING -> { rust("query_params.entry(String::from(k)).or_insert_with(|| String::from(v));") - } else -> { - rustTemplate( + } + QueryParamsTargetMapValueType.LIST, QueryParamsTargetMapValueType.SET -> { + rust( """ let entry = query_params.entry(String::from(k)).or_default(); entry.push(String::from(v)); - """.trimIndent() + """ ) } } } } if (queryParamsBinding != null) { - rust( - "input = input.${queryParamsBinding.member.deserializerBuilderSetterName( - model, - symbolProvider, - codegenContext.mode - )}(${ - if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { - "Some(query_params)" - } else { - "query_params" + val hasConstrainedTarget = + model.expectShape(queryParamsBinding.member.target, MapShape::class.java).canReachConstrainedShape(model, symbolProvider) + val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider + val isOptional = symbolProvider.toSymbol(queryParamsBinding.member).isOptional() + withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(", ");") { + conditionalBlock("Some(", ")", conditional = isOptional) { + conditionalBlock( + "#T(", + ")", + conditional = hasConstrainedTarget, + symbolProvider.toSymbol(queryParamsBinding.member).mapRustType { it.stripOuter() }) { + write("query_params") + } } - });" - ) + } +// rust( +// "input = input.${queryParamsBinding.member.deserializerBuilderSetterName( +// model, +// symbolProvider, +// codegenContext.mode +// )}(${ +// if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { +// "Some(query_params)" +// } else { +// "query_params" +// } +// });" +// ) } - queryBindingsTargettingCollection.forEach { + queryBindingsTargettingCollection.forEach { binding -> // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. val hasConstrainedTarget = - model.expectShape(it.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) + model.expectShape(binding.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider - val memberName = symbolProvider.toMemberName(it.member) - val isOptional = symbolProvider.toSymbol(it.member).isOptional() + val memberName = symbolProvider.toMemberName(binding.member) + val isOptional = symbolProvider.toSymbol(binding.member).isOptional() rustBlock("if !$memberName.is_empty()") { - withBlock("input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(", ");") { + withBlock("input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { conditionalBlock( "#T(", ")", conditional = hasConstrainedTarget, - symbolProvider.toSymbol(it.member).mapRustType { t -> t.stripOuter() }) { + symbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }) { write(memberName) } } From 587fa9c61c2da14b386fa45dc18f6f978ca8a2ec Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 18:32:16 +0200 Subject: [PATCH 079/255] support maps of list/set of constrained strings with httpQueryParams trait --- codegen-server-test/model/simple.smithy | 4 +- .../generators/UnconstrainedMapGenerator.kt | 1 + .../ServerHttpBoundProtocolGenerator.kt | 91 ++++++++----------- 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index fdc3ba69fa..b526f940fb 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -10,8 +10,8 @@ service SimpleService { operations: [ //AnOperation, QueryParamsTargetingMapOfLengthString, - // QueryParamsTargetingMapOfListOfLengthString, - // QueryParamsTargetingMapOfSetOfLengthString, + QueryParamsTargetingMapOfListOfLengthString, + QueryParamsTargetingMapOfSetOfLengthString, ], } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index badbc7095d..0968c1a060 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -52,6 +52,7 @@ class UnconstrainedMapGenerator( check(shape.canReachConstrainedShape(model, symbolProvider)) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + // TODO The unconstrained shape symbol provider should always work. val keySymbol = if (isKeyConstrained(keyShape)) { unconstrainedShapeSymbolProvider.toSymbol(keyShape) } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 0bad998359..d5ef6cd434 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -28,7 +28,6 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock -import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate @@ -624,13 +623,6 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } """ ) -// withBlock("input = input.${member.deserializerBuilderSetterName(model, symbolProvider)}(", ");") { -// if (symbolProvider.toSymbol(binding.member).isOptional()) { -// "Some(${parsedValue(this)})" -// } else { -// parsedValue(this) -// } -// } } } serverRenderUriPathParser(this, operationShape) @@ -796,13 +788,6 @@ private class ServerHttpBoundProtocolTraitImplGenerator( // * a map of set of string. enum class QueryParamsTargetMapValueType { STRING, LIST, SET; - - fun asRustType(): RustType = - when (this) { - STRING -> RustType.String - LIST -> RustType.Vec(RustType.String) - SET -> RustType.HashSet(RustType.String) - } } private fun queryParamsTargetMapValueType(targetMapValue: Shape): QueryParamsTargetMapValueType = @@ -815,8 +800,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } else { throw ExpectationNotMetException( """ - @httpQueryParams trait applied to non-supported target - $targetMapValue of type ${targetMapValue.type} + @httpQueryParams trait applied to non-supported target $targetMapValue of type ${targetMapValue.type} """.trimIndent(), targetMapValue.sourceLocation ) @@ -838,9 +822,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( fun HttpBindingDescriptor.queryParamsBindingTargetMapValueType(): QueryParamsTargetMapValueType { check(this.location == HttpLocation.QUERY_PARAMS) - val queryParamsTarget = model.expectShape(this.member.target) - val mapTarget = queryParamsTarget.asMapShape().get() - return queryParamsTargetMapValueType(model.expectShape(mapTarget.value.target)) + val queryParamsTarget = model.expectShape(this.member.target, MapShape::class.java) + return queryParamsTargetMapValueType(model.expectShape(queryParamsTarget.value.target)) } with(writer) { @@ -853,11 +836,15 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) if (queryParamsBinding != null) { - rustTemplate( - "let mut query_params: #{HashMap} = #{HashMap}::new();", - "HashMap" to RustType.HashMap.RuntimeType, - ) + val target = model.expectShape(queryParamsBinding.member.target, MapShape::class.java) + val hasConstrainedTarget = target.canReachConstrainedShape(model, symbolProvider) + // TODO Here we only check the target shape; constraint traits on member shapes are not implemented yet. + val targetSymbol = unconstrainedShapeSymbolProvider!!.toSymbol(target) + withBlock("let mut query_params: #T = ", ";", targetSymbol) { + conditionalBlock("#T(", ")", conditional = hasConstrainedTarget, targetSymbol) { + rust("#T::new()", RustType.HashMap.RuntimeType) + } + } } val (queryBindingsTargettingCollection, queryBindingsTargettingSimple) = queryBindings.partition { model.expectShape(it.member.target) is CollectionShape } @@ -933,50 +920,50 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } if (queryParamsBinding != null) { + val target = model.expectShape(queryParamsBinding.member.target, MapShape::class.java) + // TODO Here we only check the target shape; constraint traits on member shapes are not implemented yet. + val hasConstrainedTarget = target.canReachConstrainedShape(model, symbolProvider) when (queryParamsBinding.queryParamsBindingTargetMapValueType()) { QueryParamsTargetMapValueType.STRING -> { - rust("query_params.entry(String::from(k)).or_insert_with(|| String::from(v));") + rust("query_params.${if (hasConstrainedTarget) "0." else ""}entry(String::from(k)).or_insert_with(|| String::from(v));") } QueryParamsTargetMapValueType.LIST, QueryParamsTargetMapValueType.SET -> { - rust( - """ - let entry = query_params.entry(String::from(k)).or_default(); - entry.push(String::from(v)); - """ - ) + if (hasConstrainedTarget) { + val collectionShape = model.expectShape(target.value.target, CollectionShape::class.java) + val collectionSymbol = unconstrainedShapeSymbolProvider!!.toSymbol(collectionShape) + rust( + // `or_insert_with` instead of `or_insert` to avoid the allocation when the entry is + // not empty. + """ + let entry = query_params.0.entry(String::from(k)).or_insert_with(|| #T(std::vec::Vec::new())); + entry.0.push(String::from(v)); + """, + collectionSymbol, + ) + } else { + rust( + """ + let entry = query_params.entry(String::from(k)).or_default(); + entry.push(String::from(v)); + """ + ) + } } } } } if (queryParamsBinding != null) { + // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. val hasConstrainedTarget = model.expectShape(queryParamsBinding.member.target, MapShape::class.java).canReachConstrainedShape(model, symbolProvider) + // TODO Why not always use the unconstrainedShapeSymbolProvider? It should work! val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider val isOptional = symbolProvider.toSymbol(queryParamsBinding.member).isOptional() withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.mode)}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { - conditionalBlock( - "#T(", - ")", - conditional = hasConstrainedTarget, - symbolProvider.toSymbol(queryParamsBinding.member).mapRustType { it.stripOuter() }) { - write("query_params") - } + write("query_params") } } -// rust( -// "input = input.${queryParamsBinding.member.deserializerBuilderSetterName( -// model, -// symbolProvider, -// codegenContext.mode -// )}(${ -// if (symbolProvider.toSymbol(queryParamsBinding.member).isOptional()) { -// "Some(query_params)" -// } else { -// "query_params" -// } -// });" -// ) } queryBindingsTargettingCollection.forEach { binding -> // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. From 8f26ac0e4034a0ebbbea7976bd390a5b2ed24a23 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 19:06:26 +0200 Subject: [PATCH 080/255] cleanup sst model; all of this should pass but it doesn't --- codegen-server-test/model/simple.smithy | 314 +++++++++++------------- 1 file changed, 146 insertions(+), 168 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index b526f940fb..625d7e688f 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -4,125 +4,125 @@ namespace com.amazonaws.simple use aws.protocols#restJson1 +// TODO Move to another file and rename to ConstraintTraitsService. @restJson1 -@title("SimpleService") service SimpleService { operations: [ - //AnOperation, - QueryParamsTargetingMapOfLengthString, - QueryParamsTargetingMapOfListOfLengthString, - QueryParamsTargetingMapOfSetOfLengthString, + ConstrainedShapesOperation, + ConstrainedHttpBoundShapesOperation, + ConstrainedRecursiveShapesOperation, + // `httpQueryParams` is structurually exclusive, so we need one + // operation per target shape type combination. + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, ], } -@http(uri: "/operation", method: "GET") -operation AnOperation { - // input: RecursiveShapesInputOutput, - // output: RecursiveShapesInputOutput, - input: AnOperationInput, - output: AnOperationOutput, - // errors: [MyError] +@http(uri: "/constrained-shapes-operation", method: "GET") +operation ConstrainedShapesOperation { + input: ConstrainedShapesOperationInputOutput, + output: ConstrainedShapesOperationInputOutput, + errors: [ErrorWithLengthStringMessage] } -@http(uri: "/query-params-targeting-map-of-length-string", method: "GET") -operation QueryParamsTargetingMapOfLengthString { - input: QueryParamsTargetingMapOfLengthStringInputOutput, - output: QueryParamsTargetingMapOfLengthStringInputOutput, +@http(uri: "/constrained-http-bound-shapes-operation", method: "GET") +operation ConstrainedHttpBoundShapesOperation { + input: ConstrainedHttpBoundShapesOperationInputOutput, + output: ConstrainedHttpBoundShapesOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-list-of-length-string", method: "GET") -operation QueryParamsTargetingMapOfListOfLengthString { - input: QueryParamsTargetingMapOfListOfLengthStringInputOutput, - output: QueryParamsTargetingMapOfListOfLengthStringInputOutput, +@http(uri: "/constrained-recursive-shapes-operation", method: "GET") +operation ConstrainedRecursiveShapesOperation { + input: ConstrainedRecursiveShapesOperationInputOutput, + output: ConstrainedRecursiveShapesOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-set-of-length-string", method: "GET") -operation QueryParamsTargetingMapOfSetOfLengthString { - input: QueryParamsTargetingMapOfSetOfLengthStringInputOutput, - output: QueryParamsTargetingMapOfSetOfLengthStringInputOutput, +@http(uri: "/query-params-targeting-map-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfLengthStringOperation { + input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, } -structure QueryParamsTargetingMapOfLengthStringInputOutput { - @httpQueryParams - mapOfLengthString: MapOfLengthString -} - -structure QueryParamsTargetingMapOfListOfLengthStringInputOutput { - @httpQueryParams - mapOfListOfLengthString: MapOfListOfLengthString +@http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfListOfLengthStringOperation { + input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, } -structure QueryParamsTargetingMapOfSetOfLengthStringInputOutput { - @httpQueryParams - mapOfSetOfLengthString: MapOfSetOfLengthString +@http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfSetOfLengthStringOperation { + input: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } -structure AnOperationInput { +structure ConstrainedShapesOperationInputOutput { @required conA: ConA, +} - // Only top-level members of an operation's input structure are considered - // when deserializing HTTP messages. +structure ConstrainedHttpBoundShapesOperationInputOutput { + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working + // @required + @httpPrefixHeaders("X-Prefix-Headers-") + lengthStringHeaderMap: MapOfLengthString, // TODO Test with mediaType trait too. - // @httpHeader("X-Length") - // lengthStringHeader: LengthString, + @httpHeader("X-Length") + lengthStringHeader: LengthString, - // @httpHeader("X-Length-Set") - // lengthStringSetHeader: LengthStringSet, + @httpHeader("X-Length-Set") + lengthStringSetHeader: SetOfLengthString, - // @httpHeader("X-Length-List") - // lengthStringListHeader: LengthStringList, - - // // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working - // // @required - // @httpPrefixHeaders("X-Prefix-Headers-") - // lengthStringHeaderMap: LengthStringHeaderMap, + @httpHeader("X-Length-List") + lengthStringListHeader: ListOfLengthString, @httpQuery("lengthString") lengthStringQuery: LengthString, @httpQuery("lengthStringList") - lengthStringListQuery: LengthStringList, + lengthStringListQuery: ListOfLengthString, @httpQuery("lengthStringSet") - lengthStringSetQuery: LengthStringSet, + lengthStringSetQuery: SetOfLengthString, } -structure AnOperationOutput { - // conA: ConA, +structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfLengthString: MapOfLengthString +} - // conCList: ConCList, +structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfListOfLengthString: MapOfListOfLengthString } -structure ConA { - // @required - // conB: ConB, +structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfSetOfLengthString: MapOfSetOfLengthString +} - // optConB: ConB, +structure ConA { + @required + conB: ConB, - // conBList: ConBList, - // conBList2: ConBList2, + optConB: ConB, - // conBSet: ConBSet, + conBList: ConBList, + conBList2: ConBList2, - // conBMap: ConBMap + conBSet: ConBSet, - // normalString: NormalString, + conBMap: ConBMap, - // playerAction: PlayerAction, - // myEnum: MyEnum + mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - // @length(min:4, max:6) - // list: LengthList, + unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, + enumString: EnumString, - //set: LengthStringSet, -} + listOfLengthString: ListOfLengthString, -// TODO Use MapOfLengthString -map LengthStringHeaderMap { - key: LengthString, - value: LengthString, + setOfLengthString: SetOfLengthString, } map MapOfLengthString { @@ -132,38 +132,29 @@ map MapOfLengthString { map MapOfListOfLengthString { key: LengthString, - value: LengthStringList, + value: ListOfLengthString, } map MapOfSetOfLengthString { key: LengthString, - value: LengthStringSet, + value: SetOfLengthString, } -// @length(min:2, max:8) -// list LengthList { -// member: LengthString -// } +@length(min: 2, max: 8) +list LengthListOfLengthString { + member: LengthString +} -@length(min:2, max:8) +@length(min: 2, max: 69) string LengthString -string NormalString - -union PlayerAction { - /// Quit the game. - quit: Unit, - - /// Move in a specific direction. - move: DirectedAction, - - /// Jump in a specific direction. - jump: DirectedAction +union UnionWithConstrainedStructureVariant { + constrainedStructureVariant: ConstrainedStructureVariant } -structure DirectedAction { +structure ConstrainedStructureVariant { @required - direction: Integer + int: Integer } @enum([ @@ -180,31 +171,31 @@ structure DirectedAction { name: "M256_MEGA", } ]) -string MyEnum +string EnumString -// TODO Rename these to ListOfLengthString, SetOfLengthString -// A set that is not directly constrained, but that has a member that is. There -// is no such example in any of the other test models! -set LengthStringSet { +set SetOfLengthString { member: LengthString } -list LengthStringList { +list ListOfLengthString { member: LengthString } -// structure ConB { -// @required -// nice: String, -// @required -// int: Integer, -// -// optNice: String, -// optInt: Integer -// } +structure ConB { + @required + nice: String, + @required + int: Integer, + + optNice: String, + optInt: Integer +} + +structure ConstrainedRecursiveShapesOperationInputOutput { + nested: RecursiveShapesInputOutputNested1, -structure RecursiveShapesInputOutput { - nested: RecursiveShapesInputOutputNested1 + @required + recursiveList: RecursiveList } structure RecursiveShapesInputOutputNested1 { @@ -217,61 +208,48 @@ structure RecursiveShapesInputOutputNested2 { recursiveMember: RecursiveShapesInputOutputNested1, } -// list ValidList { -// member: RecursiveShapesInputOutput -// } -// -// structure RecursiveShapesInputOutput { -// @required -// foo: ValidList -// } - -// list ConBList { -// member: AnotherList -// } - -// list ConBList2 { -// member: ConB -// } - -// list ConCList { -// member: AnotherList -// } -// -// list AnotherList { -// member: ConB -// } -// -// set ConBSet { -// member: AnotherSet -// } -// -// set AnotherSet { -// member: String -// } -// - -// @length(min: 1, max: 69) -// map ConBMap { -// key: String, -// //value: AnotherMap -// value: NiceString -// } - -// @length(min: 1, max: 10) -// string NiceString - -// @error("client") -// @retryable -// @httpError(429) -// structure MyError { -// @required -// message: NiceString -// } - -// -// map AnotherMap { -// key: String, -// value: ConBList -// //value: ConB -// } +list RecursiveList { + member: RecursiveShapesInputOutputNested1 +} + +list ConBList { + member: NestedList +} + +list ConBList2 { + member: ConB +} + +list NestedList { + member: ConB +} + +set ConBSet { + member: NestedSet +} + +set NestedSet { + member: String +} + +@length(min: 1, max: 69) +map ConBMap { + key: String, + value: LengthString +} + +@error("client") +structure ErrorWithLengthStringMessage { + @required + message: LengthString +} + +map MapOfMapOfListOfListOfConB { + key: String, + value: MapOfListOfListOfConB +} + +map MapOfListOfListOfConB { + key: String, + value: ConBList +} From cda1c09423cb9ffc0ad434f3b2daa1f8700a02dd Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 18 May 2022 19:08:31 +0200 Subject: [PATCH 081/255] comment out things that don't work from sst --- codegen-server-test/model/simple.smithy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 625d7e688f..db2e343ede 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -113,11 +113,11 @@ structure ConA { conBSet: ConBSet, - conBMap: ConBMap, + // conBMap: ConBMap, mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, + //unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, enumString: EnumString, listOfLengthString: ListOfLengthString, From 4e319440774fd6b527069cafa40cfc122d388893 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 19 May 2022 14:03:14 +0200 Subject: [PATCH 082/255] Make public maps implement Deref --- .../smithy/generators/PublicConstrainedMapGenerator.kt | 9 +++++++++ .../generators/PublicConstrainedStringGenerator.kt | 3 ++- .../protocols/serialize/JsonSerializerGenerator.kt | 5 ++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt index a41f5c5b32..6d04f3923e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt @@ -42,6 +42,7 @@ class PublicConstrainedMapGenerator( } // TODO Docs for everything. + // TODO Use TryFrom from Dan's PR. writer.rustTemplate( """ ##[derive(Debug, Clone, PartialEq)] @@ -57,6 +58,14 @@ class PublicConstrainedMapGenerator( type Unconstrained = #{UnconstrainedSymbol}; } + impl std::ops::Deref for $name { + type Target = $inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::convert::TryFrom<$inner> for $name { type Error = #{ConstraintViolation}; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt index c97ca834af..41ba7f7d36 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -46,6 +46,7 @@ class PublicConstrainedStringGenerator( // TODO Docs for everything. // TODO Display impl. + // TODO Use TryFrom from Dan's PR. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait @@ -86,7 +87,7 @@ class PublicConstrainedStringGenerator( impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) + self.0.fmt(f) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index 0b590d7e0a..b42b7aced2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -416,7 +416,10 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMap(context: Context) { val keyName = safeName("key") val valueName = safeName("value") - rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { + // `iter()` is needed for when the value implements `Deref` for a `Target` that is an iterator; `Deref` coercion + // does not happen in for loops. + // This case happens when the value is a constrained type i.e. a wrapper tuple newtype holding a `HashMap`. + rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}.iter()") { val keyTarget = model.expectShape(context.shape.key.target) val keyExpression = when (keyTarget.hasTrait()) { true -> "$keyName.as_str()" From 98b5c621a3f551701d2502d5f214cf3ff0cb7855 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 19 May 2022 14:15:50 +0200 Subject: [PATCH 083/255] Make public maps work in httpHeadersPrefix --- codegen-server-test/model/simple.smithy | 31 +++++++++++++++++-- .../generators/http/HttpBindingGenerator.kt | 5 ++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index db2e343ede..8d1ca9e28e 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -11,11 +11,14 @@ service SimpleService { ConstrainedShapesOperation, ConstrainedHttpBoundShapesOperation, ConstrainedRecursiveShapesOperation, - // `httpQueryParams` is structurually exclusive, so we need one - // operation per target shape type combination. + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, QueryParamsTargetingMapOfLengthStringOperation, QueryParamsTargetingMapOfListOfLengthStringOperation, QueryParamsTargetingMapOfSetOfLengthStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, ], } @@ -38,6 +41,12 @@ operation ConstrainedRecursiveShapesOperation { output: ConstrainedRecursiveShapesOperationInputOutput, } +@http(uri: "/query-params-targeting-length-map", method: "GET") +operation QueryParamsTargetingLengthMapOperation { + input: QueryParamsTargetingLengthMapOperationInputOutput, + output: QueryParamsTargetingLengthMapOperationInputOutput, +} + @http(uri: "/query-params-targeting-map-of-length-string-operation", method: "GET") operation QueryParamsTargetingMapOfLengthStringOperation { input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, @@ -56,6 +65,12 @@ operation QueryParamsTargetingMapOfSetOfLengthStringOperation { output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } +@http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") +operation HttpPrefixHeadersTargetingLengthMapOperation { + input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, + output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, +} + structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -87,6 +102,16 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { lengthStringSetQuery: SetOfLengthString, } +structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { + @httpPrefixHeaders("X-Prefix-Headers-") + lengthMap: ConBMap, +} + +structure QueryParamsTargetingLengthMapOperationInputOutput { + @httpQueryParams + lengthMap: ConBMap +} + structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { @httpQueryParams mapOfLengthString: MapOfLengthString @@ -113,7 +138,7 @@ structure ConA { conBSet: ConBSet, - // conBMap: ConBMap, + conBMap: ConBMap, mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index ac2bc394ec..69fdbe1c04 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -542,9 +542,12 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> val listHeader = memberType is CollectionShape + // `iter()` is needed for when the value implements `Deref` for a `Target` that is an iterator; `Deref` coercion + // does not happen in for loops. + // This case happens when the value is a constrained type i.e. a wrapper tuple newtype holding a `HashMap`. rustTemplate( """ - for (k, v) in $field { + for (k, v) in $field.iter() { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} From b143866cbd4b8cc6b656c509f98c8ef7ee28a5a8 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 19 May 2022 14:22:56 +0200 Subject: [PATCH 084/255] Add MinLengthString, MaxLengthString, FixedLengthString to model --- codegen-server-test/model/simple.smithy | 14 ++++++++++++++ .../smithy/generators/UnconstrainedMapGenerator.kt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 8d1ca9e28e..a10e416634 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -133,6 +133,11 @@ structure ConA { optConB: ConB, + lengthString: LengthString, + minLengthString: MinLengthString, + maxLengthString: MaxLengthString, + fixedLengthString: FixedLengthString, + conBList: ConBList, conBList2: ConBList2, @@ -173,6 +178,15 @@ list LengthListOfLengthString { @length(min: 2, max: 69) string LengthString +@length(min: 2) +string MinLengthString + +@length(min: 69) +string MaxLengthString + +@length(min: 69, max: 69) +string FixedLengthString + union UnionWithConstrainedStructureVariant { constrainedStructureVariant: ConstrainedStructureVariant } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 0968c1a060..01a873da36 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -163,7 +163,7 @@ class UnconstrainedMapGenerator( // member: NiceString // } // - // @length(min:1, max:69) + // @length(min: 1, max: 69) // string NiceString // ``` rustTemplate( From 326cbd10248e41dec6f546d9e2365455fa0bffa1 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 19 May 2022 14:36:56 +0200 Subject: [PATCH 085/255] Appease clippy, consider allowing type_complexity --- .../smithy/generators/ServerBuilderGenerator.kt | 12 ++++++++++-- .../smithy/generators/http/HttpBindingGenerator.kt | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 734d8f7208..5d15f8d1af 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -233,7 +233,10 @@ class ServerBuilderGenerator( // We don't want to introduce API asymmetry just for this particular case, so we disable the lint. Attribute.Custom("allow(clippy::boxed_local)").render(writer) } - writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { + writer.rustBlock(""" + ##[allow(clippy::type_complexity)] + pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self + """) { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { @@ -274,9 +277,13 @@ class ServerBuilderGenerator( val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) + // TODO Ask about enabling type_complexity globally. // TODO: `pub(crate)` unless we commit to making builders of builders public. // Setter names will never hit a reserved word and therefore never need escaping. - writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { + writer.rustBlock(""" + ##[allow(clippy::type_complexity)] + pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self + """) { rust( """ self.$memberName = ${ @@ -382,6 +389,7 @@ class ServerBuilderGenerator( private fun renderTryFromBuilderImpl(writer: RustWriter) { // TODO(https://github.com/awslabs/smithy-rs/issues/1332) `TryFrom` is in Rust 2021's prelude. + // TODO Use Dan's TryFrom. writer.rustTemplate( """ impl std::convert::TryFrom for #{Structure} { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 69fdbe1c04..33323dac35 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -176,7 +176,7 @@ class HttpBindingGenerator( if (returnUnconstrainedType) { // If the map shape has constrained string keys or values, we need to wrap the deserialized hash map // in the corresponding unconstrained wrapper tuple struct. - rust("let out = out.map(|hm| #T(hm));", outputT.mapRustType { it.stripOuter() }) + rust("let out = out.map(#T);", outputT.mapRustType { it.stripOuter() }) } rust("out.map(Some)") } From 94a6fa242c69a008610d1da0e51bffe07739db62 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 19 May 2022 14:46:20 +0200 Subject: [PATCH 086/255] Add a LengthString bound to httpLabel --- codegen-server-test/model/simple.smithy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index a10e416634..90b501c0db 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -29,7 +29,7 @@ operation ConstrainedShapesOperation { errors: [ErrorWithLengthStringMessage] } -@http(uri: "/constrained-http-bound-shapes-operation", method: "GET") +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, @@ -77,6 +77,10 @@ structure ConstrainedShapesOperationInputOutput { } structure ConstrainedHttpBoundShapesOperationInputOutput { + @required + @httpLabel + lengthStringLabel: LengthString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working // @required @httpPrefixHeaders("X-Prefix-Headers-") From e3616fd22a40d4df29eedbb24bac34cefc1356a7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 14:51:17 +0200 Subject: [PATCH 087/255] dont implement Deref or AsRef, rely on .0; add inner() --- codegen-server-test/model/simple.smithy | 8 +++++- .../PublicConstrainedMapGenerator.kt | 14 ++++------ .../PublicConstrainedStringGenerator.kt | 20 ++++--------- .../smithy/rust/codegen/smithy/Constraints.kt | 11 ++++++-- .../generators/http/HttpBindingGenerator.kt | 17 +++++++---- .../serialize/JsonSerializerGenerator.kt | 28 +++++++++++++------ 6 files changed, 58 insertions(+), 40 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 90b501c0db..f9c10d77bd 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -86,10 +86,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpPrefixHeaders("X-Prefix-Headers-") lengthStringHeaderMap: MapOfLengthString, - // TODO Test with mediaType trait too. @httpHeader("X-Length") lengthStringHeader: LengthString, + // @httpHeader("X-Length-MediaType") + // lengthStringHeaderWithMediaType: MediaTypeLengthString, + @httpHeader("X-Length-Set") lengthStringSetHeader: SetOfLengthString, @@ -191,6 +193,10 @@ string MaxLengthString @length(min: 69, max: 69) string FixedLengthString +@mediaType("video/quicktime") +@length(min: 1, max: 69) +string MediaTypeLengthString + union UnionWithConstrainedStructureVariant { constrainedStructureVariant: ConstrainedStructureVariant } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt index 6d04f3923e..78ab074ed9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt @@ -46,26 +46,22 @@ class PublicConstrainedMapGenerator( writer.rustTemplate( """ ##[derive(Debug, Clone, PartialEq)] - pub struct $name($inner); + pub struct $name(pub(crate) $inner); impl $name { pub fn parse(value: $inner) -> Result { Self::try_from(value) } + + pub fn inner(&self) -> &$inner { + &self.0 + } } impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; } - impl std::ops::Deref for $name { - type Target = $inner; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - impl std::convert::TryFrom<$inner> for $name { type Error = #{ConstraintViolation}; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt index 41ba7f7d36..927b507397 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt @@ -53,12 +53,16 @@ class PublicConstrainedStringGenerator( writer.rustTemplate( """ ##[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name($inner); + pub struct $name(pub(crate) $inner); impl $name { pub fn parse(value: $inner) -> Result { Self::try_from(value) } + + pub fn inner(&self) -> &$inner { + &self.0 + } } impl #{ConstrainedTrait} for $name { @@ -71,20 +75,6 @@ class PublicConstrainedStringGenerator( } } - impl std::ops::Deref for $name { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl AsRef for $name { - fn as_ref(&self) -> &str { - self.0.as_str() - } - } - impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 851840128c..767ee57517 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -48,6 +48,13 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { else -> false } +fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (this) { + is MapShape -> this.hasTrait() + is StringShape -> !this.hasTrait() && this.hasTrait() + is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model) + else -> false +} + fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } @@ -69,8 +76,8 @@ fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolPro fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = when (val targetShape = model.expectShape(this.target)) { is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> targetShape.asMapShape().get().canReachConstrainedShape(model, symbolProvider) - is StructureShape -> targetShape.asStructureShape().get().canReachConstrainedShape(model, symbolProvider) + is MapShape -> targetShape.canReachConstrainedShape(model, symbolProvider) + is StructureShape -> targetShape.canReachConstrainedShape(model, symbolProvider) else -> targetShape.isDirectlyConstrained(symbolProvider) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 33323dac35..855cb2587a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -41,6 +41,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.generators.redactIfNecessary +import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor @@ -542,12 +543,11 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> val listHeader = memberType is CollectionShape - // `iter()` is needed for when the value implements `Deref` for a `Target` that is an iterator; `Deref` coercion - // does not happen in for loops. - // This case happens when the value is a constrained type i.e. a wrapper tuple newtype holding a `HashMap`. + val workingWithPublicConstrainedWrapperTupleType = + mode == CodegenMode.Server && memberShape.hasPublicConstrainedWrapperTupleType(model) rustTemplate( """ - for (k, v) in $field.iter() { + for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType) "&$field.0" else field } { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} @@ -592,7 +592,14 @@ class HttpBindingGenerator( val func = writer.format(RuntimeType.Base64Encode(runtimeConfig)) "$func(&$targetName)" } else { - quoteValue("AsRef::::as_ref($targetName)") + // TODO Constraint traits on member traits are not supported yet. Note that here, counterintuitively, + // in case we're rendering a header for a collection, `member` is still referring to the structure + // member shape where the `httpHeader` or `httpPrefixHeaders` trait was found, and _not_ to the + // collection member shape on which we'd have to check for constraint trait precedence. So + // `member.hasPublicConstrainedWrapperTupleType()` is _not_ what we want. + val workingWithPublicConstrainedWrapperTupleType = + mode == CodegenMode.Server && target.hasPublicConstrainedWrapperTupleType(model) + quoteValue("AsRef::::as_ref(${ if (workingWithPublicConstrainedWrapperTupleType) "&$targetName.0" else targetName })") } } target.isTimestampShape -> { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index b42b7aced2..74d9d2a8aa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -32,6 +32,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.customize.NamedSectionGenerator @@ -39,6 +40,7 @@ import software.amazon.smithy.rust.codegen.smithy.customize.Section import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.generators.serializationError +import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation @@ -347,7 +349,15 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMemberValue(context: MemberContext, target: Shape) { val writer = context.writerExpression - val value = context.valueExpression + + val workingWithPublicConstrainedWrapperTupleType = + mode == CodegenMode.Server && context.shape.hasPublicConstrainedWrapperTupleType(model) + val value = if (workingWithPublicConstrainedWrapperTupleType) { + ValueExpression.Value("${context.valueExpression.name}.0") + } else { + context.valueExpression + } + when (target) { is StringShape -> rust("$writer.string(${value.name}.as_str());") is BooleanShape -> rust("$writer.boolean(${value.asValue()});") @@ -416,14 +426,16 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMap(context: Context) { val keyName = safeName("key") val valueName = safeName("value") - // `iter()` is needed for when the value implements `Deref` for a `Target` that is an iterator; `Deref` coercion - // does not happen in for loops. - // This case happens when the value is a constrained type i.e. a wrapper tuple newtype holding a `HashMap`. - rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}.iter()") { + rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { val keyTarget = model.expectShape(context.shape.key.target) - val keyExpression = when (keyTarget.hasTrait()) { - true -> "$keyName.as_str()" - else -> keyName + val workingWithPublicConstrainedWrapperTupleType = + mode == CodegenMode.Server && keyTarget.hasPublicConstrainedWrapperTupleType(model) + val keyExpression = if (workingWithPublicConstrainedWrapperTupleType) { + "$keyName.0.as_str()" + } else if (keyTarget.hasTrait()) { + "$keyName.as_str()" + } else { + keyName } serializeMember(MemberContext.mapMember(context, keyExpression, valueName)) } From 3b5e751548de3de8b75969f11c0ee2bd64b08aa5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 16:10:30 +0200 Subject: [PATCH 088/255] enable allow type_complexity globally, everything except XML works --- .../smithy/generators/ServerBuilderGenerator.kt | 11 ++--------- .../rust/codegen/smithy/generators/LibRsGenerator.kt | 2 ++ .../smithy/protocols/parse/JsonParserGenerator.kt | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 5d15f8d1af..b864608ad8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -233,10 +233,7 @@ class ServerBuilderGenerator( // We don't want to introduce API asymmetry just for this particular case, so we disable the lint. Attribute.Custom("allow(clippy::boxed_local)").render(writer) } - writer.rustBlock(""" - ##[allow(clippy::type_complexity)] - pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self - """) { + writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { @@ -277,13 +274,9 @@ class ServerBuilderGenerator( val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) - // TODO Ask about enabling type_complexity globally. // TODO: `pub(crate)` unless we commit to making builders of builders public. // Setter names will never hit a reserved word and therefore never need escaping. - writer.rustBlock(""" - ##[allow(clippy::type_complexity)] - pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self - """) { + writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { rust( """ self.$memberName = ${ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt index b16a581650..9f85d7c49d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt @@ -44,6 +44,8 @@ class LibRsGenerator( rust("##![warn(missing_docs)]") } + rust("##![allow(clippy::type_complexity)]") + val libraryDocs = settings.getService(model).getTrait()?.value ?: settings.moduleName containerDocs(escape(libraryDocs)) val crateLayout = customizations.map { it.section(LibRsSection.ModuleDocumentation(LibRsSection.CrateOrganization)) }.filter { !it.isEmpty() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 46dd7530bf..3f31458039 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -310,7 +310,7 @@ class JsonParserGenerator( // which become `__list_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. it.rustBlockTemplate( """ - ##[allow(clippy::type_complexity, non_snake_case)] + ##[allow(non_snake_case)] pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, @@ -366,7 +366,7 @@ class JsonParserGenerator( // which become `__map_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. it.rustBlockTemplate( """ - ##[allow(clippy::type_complexity, non_snake_case)] + ##[allow(non_snake_case)] pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, From 51c83b6278f2a9e15a686abc336d32dbe240beba Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 16:24:41 +0200 Subject: [PATCH 089/255] Fix XML --- .../XmlBindingTraitSerializerGenerator.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index fed0e30aa6..a20cbfea65 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -19,7 +19,6 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.model.traits.XmlNamespaceTrait @@ -38,10 +37,12 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.generators.serializationError +import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver @@ -286,11 +287,15 @@ class XmlBindingTraitSerializerGenerator( } private fun RustWriter.serializeRawMember(member: MemberShape, input: String) { - when (val shape = model.expectShape(member.target)) { - is StringShape -> if (shape.hasTrait()) { - rust("$input.as_str()") - } else { - rust("$input.as_ref()") + when (model.expectShape(member.target)) { + is StringShape -> { + val workingWithPublicConstrainedWrapperTupleType = + mode == CodegenMode.Server && member.hasPublicConstrainedWrapperTupleType(model) + if (workingWithPublicConstrainedWrapperTupleType) { + rust("$input.0.as_str()") + } else { + rust("$input.as_str()") + } } is BooleanShape, is NumberShape -> { rust( From 893060e71f5ae00167816c39ba48fafc2455a082 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 16:34:15 +0200 Subject: [PATCH 090/255] Add constraints model and don't use simple anymore --- codegen-server-test/build.gradle.kts | 1 + codegen-server-test/model/constraints.smithy | 305 +++++++++++++++++ codegen-server-test/model/simple.smithy | 343 +++++-------------- codegen-test/build.gradle.kts | 1 + codegen-test/model/constraints.smithy | 1 + 5 files changed, 393 insertions(+), 258 deletions(-) create mode 100644 codegen-server-test/model/constraints.smithy create mode 120000 codegen-test/model/constraints.smithy diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index ce9ca74015..91ca10d58a 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), + CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy new file mode 100644 index 0000000000..87e069e97b --- /dev/null +++ b/codegen-server-test/model/constraints.smithy @@ -0,0 +1,305 @@ +$version: "1.0" + +namespace com.amazonaws.simple + +use aws.protocols#restJson1 + +/// A service to test aspects of code generation where shapes have constraint traits. +@restJson1 +@title("ConstraintsService") +service ConstraintsService { + operations: [ + ConstrainedShapesOperation, + ConstrainedHttpBoundShapesOperation, + ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, + ], +} + +@http(uri: "/constrained-shapes-operation", method: "GET") +operation ConstrainedShapesOperation { + input: ConstrainedShapesOperationInputOutput, + output: ConstrainedShapesOperationInputOutput, + errors: [ErrorWithLengthStringMessage] +} + +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") +operation ConstrainedHttpBoundShapesOperation { + input: ConstrainedHttpBoundShapesOperationInputOutput, + output: ConstrainedHttpBoundShapesOperationInputOutput, +} + +@http(uri: "/constrained-recursive-shapes-operation", method: "GET") +operation ConstrainedRecursiveShapesOperation { + input: ConstrainedRecursiveShapesOperationInputOutput, + output: ConstrainedRecursiveShapesOperationInputOutput, +} + +@http(uri: "/query-params-targeting-length-map", method: "GET") +operation QueryParamsTargetingLengthMapOperation { + input: QueryParamsTargetingLengthMapOperationInputOutput, + output: QueryParamsTargetingLengthMapOperationInputOutput, +} + +@http(uri: "/query-params-targeting-map-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfLengthStringOperation { + input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, +} + +@http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfListOfLengthStringOperation { + input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, +} + +@http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "GET") +operation QueryParamsTargetingMapOfSetOfLengthStringOperation { + input: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, + output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, +} + +@http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") +operation HttpPrefixHeadersTargetingLengthMapOperation { + input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, + output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, +} + +structure ConstrainedShapesOperationInputOutput { + @required + conA: ConA, +} + +structure ConstrainedHttpBoundShapesOperationInputOutput { + @required + @httpLabel + lengthStringLabel: LengthString, + + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working + // @required + @httpPrefixHeaders("X-Prefix-Headers-") + lengthStringHeaderMap: MapOfLengthString, + + @httpHeader("X-Length") + lengthStringHeader: LengthString, + + // @httpHeader("X-Length-MediaType") + // lengthStringHeaderWithMediaType: MediaTypeLengthString, + + @httpHeader("X-Length-Set") + lengthStringSetHeader: SetOfLengthString, + + @httpHeader("X-Length-List") + lengthStringListHeader: ListOfLengthString, + + @httpQuery("lengthString") + lengthStringQuery: LengthString, + + @httpQuery("lengthStringList") + lengthStringListQuery: ListOfLengthString, + + @httpQuery("lengthStringSet") + lengthStringSetQuery: SetOfLengthString, +} + +structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { + @httpPrefixHeaders("X-Prefix-Headers-") + lengthMap: ConBMap, +} + +structure QueryParamsTargetingLengthMapOperationInputOutput { + @httpQueryParams + lengthMap: ConBMap +} + +structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfLengthString: MapOfLengthString +} + +structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfListOfLengthString: MapOfListOfLengthString +} + +structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { + @httpQueryParams + mapOfSetOfLengthString: MapOfSetOfLengthString +} + +structure ConA { + @required + conB: ConB, + + optConB: ConB, + + lengthString: LengthString, + minLengthString: MinLengthString, + maxLengthString: MaxLengthString, + fixedLengthString: FixedLengthString, + + conBList: ConBList, + conBList2: ConBList2, + + conBSet: ConBSet, + + conBMap: ConBMap, + + mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, + + //unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, + enumString: EnumString, + + listOfLengthString: ListOfLengthString, + + setOfLengthString: SetOfLengthString, +} + +map MapOfLengthString { + key: LengthString, + value: LengthString, +} + +map MapOfListOfLengthString { + key: LengthString, + value: ListOfLengthString, +} + +map MapOfSetOfLengthString { + key: LengthString, + value: SetOfLengthString, +} + +@length(min: 2, max: 8) +list LengthListOfLengthString { + member: LengthString +} + +@length(min: 2, max: 69) +string LengthString + +@length(min: 2) +string MinLengthString + +@length(min: 69) +string MaxLengthString + +@length(min: 69, max: 69) +string FixedLengthString + +@mediaType("video/quicktime") +@length(min: 1, max: 69) +string MediaTypeLengthString + +union UnionWithConstrainedStructureVariant { + constrainedStructureVariant: ConstrainedStructureVariant +} + +structure ConstrainedStructureVariant { + @required + int: Integer +} + +@enum([ + { + value: "t2.nano", + name: "T2_NANO", + }, + { + value: "t2.micro", + name: "T2_MICRO", + }, + { + value: "m256.mega", + name: "M256_MEGA", + } +]) +string EnumString + +set SetOfLengthString { + member: LengthString +} + +list ListOfLengthString { + member: LengthString +} + +structure ConB { + @required + nice: String, + @required + int: Integer, + + optNice: String, + optInt: Integer +} + +structure ConstrainedRecursiveShapesOperationInputOutput { + nested: RecursiveShapesInputOutputNested1, + + @required + recursiveList: RecursiveList +} + +structure RecursiveShapesInputOutputNested1 { + @required + recursiveMember: RecursiveShapesInputOutputNested2 +} + +structure RecursiveShapesInputOutputNested2 { + @required + recursiveMember: RecursiveShapesInputOutputNested1, +} + +list RecursiveList { + member: RecursiveShapesInputOutputNested1 +} + +list ConBList { + member: NestedList +} + +list ConBList2 { + member: ConB +} + +list NestedList { + member: ConB +} + +set ConBSet { + member: NestedSet +} + +set NestedSet { + member: String +} + +@length(min: 1, max: 69) +map ConBMap { + key: String, + value: LengthString +} + +@error("client") +structure ErrorWithLengthStringMessage { + @required + message: LengthString +} + +map MapOfMapOfListOfListOfConB { + key: String, + value: MapOfListOfListOfConB +} + +map MapOfListOfListOfConB { + key: String, + value: ConBList +} diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index f9c10d77bd..d4420787fe 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -3,302 +3,129 @@ $version: "1.0" namespace com.amazonaws.simple use aws.protocols#restJson1 +use smithy.test#httpRequestTests +use smithy.test#httpResponseTests -// TODO Move to another file and rename to ConstraintTraitsService. @restJson1 +@title("SimpleService") +@documentation("A simple service example, with a Service resource that can be registered and a readonly healthcheck") service SimpleService { + version: "2022-01-01", + resources: [ + Service, + ], operations: [ - ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, + Healthcheck, + StoreServiceBlob, ], } -@http(uri: "/constrained-shapes-operation", method: "GET") -operation ConstrainedShapesOperation { - input: ConstrainedShapesOperationInputOutput, - output: ConstrainedShapesOperationInputOutput, - errors: [ErrorWithLengthStringMessage] -} - -@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") -operation ConstrainedHttpBoundShapesOperation { - input: ConstrainedHttpBoundShapesOperationInputOutput, - output: ConstrainedHttpBoundShapesOperationInputOutput, -} - -@http(uri: "/constrained-recursive-shapes-operation", method: "GET") -operation ConstrainedRecursiveShapesOperation { - input: ConstrainedRecursiveShapesOperationInputOutput, - output: ConstrainedRecursiveShapesOperationInputOutput, -} - -@http(uri: "/query-params-targeting-length-map", method: "GET") -operation QueryParamsTargetingLengthMapOperation { - input: QueryParamsTargetingLengthMapOperationInputOutput, - output: QueryParamsTargetingLengthMapOperationInputOutput, -} - -@http(uri: "/query-params-targeting-map-of-length-string-operation", method: "GET") -operation QueryParamsTargetingMapOfLengthStringOperation { - input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, - output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, -} - -@http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") -operation QueryParamsTargetingMapOfListOfLengthStringOperation { - input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, - output: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, -} - -@http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "GET") -operation QueryParamsTargetingMapOfSetOfLengthStringOperation { - input: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, - output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, -} - -@http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") -operation HttpPrefixHeadersTargetingLengthMapOperation { - input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, - output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, -} - -structure ConstrainedShapesOperationInputOutput { - @required - conA: ConA, -} - -structure ConstrainedHttpBoundShapesOperationInputOutput { - @required - @httpLabel - lengthStringLabel: LengthString, - - // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working - // @required - @httpPrefixHeaders("X-Prefix-Headers-") - lengthStringHeaderMap: MapOfLengthString, +@documentation("Id of the service that will be registered") +string ServiceId - @httpHeader("X-Length") - lengthStringHeader: LengthString, +@documentation("Name of the service that will be registered") +string ServiceName - // @httpHeader("X-Length-MediaType") - // lengthStringHeaderWithMediaType: MediaTypeLengthString, - - @httpHeader("X-Length-Set") - lengthStringSetHeader: SetOfLengthString, - - @httpHeader("X-Length-List") - lengthStringListHeader: ListOfLengthString, - - @httpQuery("lengthString") - lengthStringQuery: LengthString, - - @httpQuery("lengthStringList") - lengthStringListQuery: ListOfLengthString, - - @httpQuery("lengthStringSet") - lengthStringSetQuery: SetOfLengthString, -} - -structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { - @httpPrefixHeaders("X-Prefix-Headers-") - lengthMap: ConBMap, -} - -structure QueryParamsTargetingLengthMapOperationInputOutput { - @httpQueryParams - lengthMap: ConBMap -} - -structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { - @httpQueryParams - mapOfLengthString: MapOfLengthString -} - -structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { - @httpQueryParams - mapOfListOfLengthString: MapOfListOfLengthString -} - -structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { - @httpQueryParams - mapOfSetOfLengthString: MapOfSetOfLengthString -} - -structure ConA { +@error("client") +@documentation( + """ + Returned when a new resource cannot be created because one already exists. + """ +) +structure ResourceAlreadyExists { @required - conB: ConB, - - optConB: ConB, - - lengthString: LengthString, - minLengthString: MinLengthString, - maxLengthString: MaxLengthString, - fixedLengthString: FixedLengthString, - - conBList: ConBList, - conBList2: ConBList2, - - conBSet: ConBSet, - - conBMap: ConBMap, - - mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - - //unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, - enumString: EnumString, - - listOfLengthString: ListOfLengthString, - - setOfLengthString: SetOfLengthString, -} - -map MapOfLengthString { - key: LengthString, - value: LengthString, -} - -map MapOfListOfLengthString { - key: LengthString, - value: ListOfLengthString, + message: String } -map MapOfSetOfLengthString { - key: LengthString, - value: SetOfLengthString, +@documentation("A resource that can register services") +resource Service { + identifiers: { id: ServiceId }, + put: RegisterService, } -@length(min: 2, max: 8) -list LengthListOfLengthString { - member: LengthString -} - -@length(min: 2, max: 69) -string LengthString - -@length(min: 2) -string MinLengthString - -@length(min: 69) -string MaxLengthString - -@length(min: 69, max: 69) -string FixedLengthString - -@mediaType("video/quicktime") -@length(min: 1, max: 69) -string MediaTypeLengthString - -union UnionWithConstrainedStructureVariant { - constrainedStructureVariant: ConstrainedStructureVariant -} - -structure ConstrainedStructureVariant { - @required - int: Integer -} - -@enum([ +@idempotent +@http(method: "PUT", uri: "/service/{id}") +@documentation("Service register operation") +@httpRequestTests([ { - value: "t2.nano", - name: "T2_NANO", - }, - { - value: "t2.micro", - name: "T2_MICRO", - }, + id: "RegisterServiceRequestTest", + protocol: "aws.protocols#restJson1", + uri: "/service/1", + headers: { + "Content-Type": "application/json", + }, + params: { id: "1", name: "TestService" }, + body: "{\"name\":\"TestService\"}", + method: "PUT", + } +]) +@httpResponseTests([ { - value: "m256.mega", - name: "M256_MEGA", + id: "RegisterServiceResponseTest", + protocol: "aws.protocols#restJson1", + params: { id: "1", name: "TestService" }, + body: "{\"id\":\"1\",\"name\":\"TestService\"}", + code: 200, } ]) -string EnumString - -set SetOfLengthString { - member: LengthString -} - -list ListOfLengthString { - member: LengthString -} - -structure ConB { - @required - nice: String, - @required - int: Integer, - - optNice: String, - optInt: Integer -} - -structure ConstrainedRecursiveShapesOperationInputOutput { - nested: RecursiveShapesInputOutputNested1, - - @required - recursiveList: RecursiveList +operation RegisterService { + input: RegisterServiceInputRequest, + output: RegisterServiceOutputResponse, + errors: [ResourceAlreadyExists] } -structure RecursiveShapesInputOutputNested1 { +@documentation("Service register input structure") +structure RegisterServiceInputRequest { @required - recursiveMember: RecursiveShapesInputOutputNested2 + @httpLabel + id: ServiceId, + name: ServiceName, } -structure RecursiveShapesInputOutputNested2 { +@documentation("Service register output structure") +structure RegisterServiceOutputResponse { @required - recursiveMember: RecursiveShapesInputOutputNested1, + id: ServiceId, + name: ServiceName, } -list RecursiveList { - member: RecursiveShapesInputOutputNested1 +@readonly +@http(uri: "/healthcheck", method: "GET") +@documentation("Read-only healthcheck operation") +operation Healthcheck { + input: HealthcheckInputRequest, + output: HealthcheckOutputResponse } -list ConBList { - member: NestedList -} +@documentation("Service healthcheck output structure") +structure HealthcheckInputRequest { -list ConBList2 { - member: ConB } -list NestedList { - member: ConB -} +@documentation("Service healthcheck input structure") +structure HealthcheckOutputResponse { -set ConBSet { - member: NestedSet } -set NestedSet { - member: String +@readonly +@http(method: "GET", uri: "/service/{id}/blob") +@documentation("Stores a blob for a service id") +operation StoreServiceBlob { + input: StoreServiceBlobInput, + output: StoreServiceBlobOutput } -@length(min: 1, max: 69) -map ConBMap { - key: String, - value: LengthString -} - -@error("client") -structure ErrorWithLengthStringMessage { +@documentation("Store a blob for a service id input structure") +structure StoreServiceBlobInput { @required - message: LengthString + @httpLabel + id: ServiceId, + @required + @httpPayload + content: Blob, } -map MapOfMapOfListOfListOfConB { - key: String, - value: MapOfListOfListOfConB -} +@documentation("Store a blob for a service id output structure") +structure StoreServiceBlobOutput { -map MapOfListOfListOfConB { - key: String, - value: ConBList } diff --git a/codegen-test/build.gradle.kts b/codegen-test/build.gradle.kts index e9755f9500..3e4b6ce678 100644 --- a/codegen-test/build.gradle.kts +++ b/codegen-test/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), + CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), CodegenTest("com.amazonaws.ebs#Ebs", "ebs"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), diff --git a/codegen-test/model/constraints.smithy b/codegen-test/model/constraints.smithy new file mode 120000 index 0000000000..08b77ecb24 --- /dev/null +++ b/codegen-test/model/constraints.smithy @@ -0,0 +1 @@ +../../codegen-server-test/model/constraints.smithy \ No newline at end of file From fb21f9327c4e80a02c227004e1f0ce3cddf164b2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 16:52:57 +0200 Subject: [PATCH 091/255] rename everything --- ...r.kt => ConstrainedShapeSymbolProvider.kt} | 2 +- .../server/smithy/RustCodegenServerPlugin.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 36 ++--- ...=> ConstrainedCollectionShapeGenerator.kt} | 10 +- .../generators/ConstrainedMapGenerator.kt | 125 +++++++----------- ...rator.kt => ConstrainedStringGenerator.kt} | 2 +- .../PubCrateConstrainedMapGenerator.kt | 122 +++++++++++++++++ .../PublicConstrainedMapGenerator.kt | 85 ------------ .../generators/ServerBuilderGenerator.kt | 8 +- .../UnconstrainedCollectionGenerator.kt | 6 +- .../generators/UnconstrainedMapGenerator.kt | 8 +- .../UnconstrainedCollectionGeneratorTest.kt | 10 +- .../UnconstrainedMapGeneratorTest.kt | 10 +- ...PubCrateConstrainedShapeSymbolProvider.kt} | 2 +- 14 files changed, 219 insertions(+), 209 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/{PublicConstrainedShapeSymbolProvider.kt => ConstrainedShapeSymbolProvider.kt} (99%) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{ConstrainedCollectionShape.kt => ConstrainedCollectionShapeGenerator.kt} (91%) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{PublicConstrainedStringGenerator.kt => ConstrainedStringGenerator.kt} (99%) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt delete mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt rename codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/{ConstrainedShapeSymbolProvider.kt => PubCrateConstrainedShapeSymbolProvider.kt} (99%) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt similarity index 99% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 307c99a06a..2c1c33a047 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PublicConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -33,7 +33,7 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase // TODO Docs. This symbol provider is wrapped by the other ones. // TODO Unit tests. -class PublicConstrainedShapeSymbolProvider( +class ConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, private val serviceShape: ServiceShape, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 318a702e90..62e80b35a1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -61,7 +61,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs - .let { if (publicConstrainedShapesEnabled) PublicConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + .let { if (publicConstrainedShapesEnabled) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 73d6c1475f..8e963e5e5e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -20,10 +20,10 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedCollectionShape +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedCollectionShapeGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedMapGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.PublicConstrainedStringGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -33,9 +33,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtoco import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenMode import software.amazon.smithy.rust.codegen.smithy.Constrained -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider @@ -76,7 +76,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val symbolProvider: RustSymbolProvider private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider private val rustCrate: RustCrate private val fileManifest = context.fileManifest @@ -126,7 +126,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ) ), model, service ) - constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, service) + pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) codegenContext = CodegenContext( @@ -224,7 +224,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: val builderGenerator = ServerBuilderGenerator( codegenContext, shape, - if (shapesReachableFromOperationInputs.contains(shape)) constrainedShapeSymbolProvider else null + if (shapesReachableFromOperationInputs.contains(shape)) pubCrateConstrainedShapeSymbolProvider else null ) builderGenerator.render(writer) writer.implBlock(shape, symbolProvider) { @@ -249,7 +249,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape @@ -258,11 +258,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for list $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedCollectionShape( + ConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, writer, shape ).render() @@ -282,7 +282,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape @@ -291,11 +291,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for set $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedCollectionShape( + ConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, writer, shape ).render() @@ -306,7 +306,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: override fun mapShape(shape: MapShape) { if (shape.isDirectlyConstrained(symbolProvider)) { rustCrate.useShapeWriter(shape) { writer -> - PublicConstrainedMapGenerator( + ConstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, @@ -328,7 +328,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, shape @@ -338,11 +338,11 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: if (!shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedMapGenerator( + PubCrateConstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, writer, shape ).render() @@ -376,7 +376,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> - PublicConstrainedStringGenerator( + ConstrainedStringGenerator( model, symbolProvider, constraintViolationSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt similarity index 91% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt index 933dc9efad..04948ad7e2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShape.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -20,11 +20,11 @@ import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained // TODO Docs // TODO Unit tests -class ConstrainedCollectionShape( +class ConstrainedCollectionShapeGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: CollectionShape ) { @@ -32,12 +32,12 @@ class ConstrainedCollectionShape( check(shape.canReachConstrainedShape(model, symbolProvider)) val symbol = symbolProvider.toSymbol(shape) - val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) - val innerConstrainedSymbol = constrainedShapeSymbolProvider.toSymbol(innerShape) + val innerConstrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, // then its corresponding constrained type is the `pub(crate)` wrapper tuple type, which needs converting into diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 5a68d44eee..bb3a39f6c8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -6,21 +6,15 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape -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.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.util.expectTrait // TODO Docs // TODO Unit tests @@ -28,85 +22,64 @@ class ConstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: MapShape ) { fun render() { - check(shape.canReachConstrainedShape(model, symbolProvider)) + val lengthTrait = shape.expectTrait() - val symbol = symbolProvider.toSymbol(shape) - val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) - val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) - val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() - val name = constrainedSymbol.name - val keyShape = model.expectShape(shape.key.target, StringShape::class.java) - val valueShape = model.expectShape(shape.value.target) - val keySymbol = if (isKeyConstrained(keyShape)) { - constrainedShapeSymbolProvider.toSymbol(keyShape) - } else { - symbolProvider.toSymbol(keyShape) - } - val valueSymbol = if (isValueConstrained(valueShape)) { - constrainedShapeSymbolProvider.toSymbol(valueShape) + val name = symbolProvider.toSymbol(shape).name + val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" + val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) + + val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { + "(${lengthTrait.min.get()}..=${lengthTrait.max.get()}).contains(&length)" + } else if (lengthTrait.min.isPresent) { + "${lengthTrait.min.get()} <= length" } else { - symbolProvider.toSymbol(valueShape) + "length <= ${lengthTrait.max.get()}" } - // Unless the map holds an aggregate shape as its value shape whose symbol's type is _not_ `pub(crate)`, the - // `.into()` calls are useless. - // See the comment in [ConstrainedCollectionShape] for a more detailed explanation. - val innerNeedsConstraining = - !valueShape.isDirectlyConstrained(symbolProvider) && (valueShape is CollectionShape || valueShape.isMapShape) - - writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { - rustTemplate( - """ - ##[derive(Debug, Clone)] - pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); - - impl #{ConstrainedTrait} for $name { - type Unconstrained = #{UnconstrainedSymbol}; + // TODO Docs for everything. + // TODO Use TryFrom from Dan's PR. + writer.rustTemplate( + """ + ##[derive(Debug, Clone, PartialEq)] + pub struct $name(pub(crate) $inner); + + impl $name { + pub fn parse(value: $inner) -> Result { + Self::try_from(value) } - impl From<#{Symbol}> for $name { - fn from(v: #{Symbol}) -> Self { - ${ if (innerNeedsConstraining) { - "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" - } else { - "Self(v)" - } } - } + pub fn inner(&self) -> &$inner { + &self.0 } - - impl From<$name> for #{Symbol} { - fn from(v: $name) -> Self { - ${ if (innerNeedsConstraining) { - "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" - } else { - "v.0" - } } + } + + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + + impl std::convert::TryFrom<$inner> for $name { + type Error = #{ConstraintViolation}; + + fn try_from(value: $inner) -> Result { + let length = value.len(); + if $condition { + Ok(Self(value)) + } else { + Err(#{ConstraintViolation}::Length(length)) } } - """, - "KeySymbol" to keySymbol, - "ValueSymbol" to valueSymbol, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "UnconstrainedSymbol" to unconstrainedSymbol, - "Symbol" to symbol, - ) - } - } - - // TODO These are copied from `UnconstrainedMapGenerator.kt`. - private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) - - private fun isValueConstrained(shape: Shape): Boolean = when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) - is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - is StringShape -> shape.isDirectlyConstrained(symbolProvider) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. - else -> false + } + """, + "KeySymbol" to symbolProvider.toSymbol(model.expectShape(shape.key.target)), + "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedShapeSymbolProvider.toSymbol(shape), + "ConstraintViolation" to constraintViolation + ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt similarity index 99% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 927b507397..5e7e8e0f19 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Docs // TODO Unit tests -class PublicConstrainedStringGenerator( +class ConstrainedStringGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt new file mode 100644 index 0000000000..7ab8dd5008 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -0,0 +1,122 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape +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.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained + +// TODO Unit tests +/** + * A generator for a wrapper tuple newtype over a map shape's symbol type. + * + * This newtype is for a map shape that is _transitively_ constrained, but not directly. That is, the map shape + * does not have a constraint trait attached, but the keys and/or values it holds reach a constrained shape. The + * generated newtype is therefore `pub(crate)`, as the class name indicates, and is not available to end users. After + * deserialization, upon constraint traits' enforcement, this type is converted into the regular `HashMap` the user sees + * via the generated converters. + * + * If the map shape is _directly_ constrained, use [ConstrainedMapGenerator] instead. + */ +class PubCrateConstrainedMapGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, + val writer: RustWriter, + val shape: MapShape +) { + fun render() { + check(shape.canReachConstrainedShape(model, symbolProvider)) + + val symbol = symbolProvider.toSymbol(shape) + val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) + val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() + val name = constrainedSymbol.name + val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + val valueShape = model.expectShape(shape.value.target) + val keySymbol = if (isKeyConstrained(keyShape)) { + pubCrateConstrainedShapeSymbolProvider.toSymbol(keyShape) + } else { + symbolProvider.toSymbol(keyShape) + } + val valueSymbol = if (isValueConstrained(valueShape)) { + pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) + } else { + symbolProvider.toSymbol(valueShape) + } + + // Unless the map holds an aggregate shape as its value shape whose symbol's type is _not_ `pub(crate)`, the + // `.into()` calls are useless. + // See the comment in [ConstrainedCollectionShape] for a more detailed explanation. + val innerNeedsConstraining = + !valueShape.isDirectlyConstrained(symbolProvider) && (valueShape is CollectionShape || valueShape.isMapShape) + + writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + + impl From<#{Symbol}> for $name { + fn from(v: #{Symbol}) -> Self { + ${ if (innerNeedsConstraining) { + "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" + } else { + "Self(v)" + } } + } + } + + impl From<$name> for #{Symbol} { + fn from(v: $name) -> Self { + ${ if (innerNeedsConstraining) { + "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" + } else { + "v.0" + } } + } + } + """, + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, + "Symbol" to symbol, + ) + } + } + + // TODO These are copied from `UnconstrainedMapGenerator.kt`. + private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) + + private fun isValueConstrained(shape: Shape): Boolean = when (shape) { + is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) + is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) + is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) + is StringShape -> shape.isDirectlyConstrained(symbolProvider) + // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. + else -> false + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt deleted file mode 100644 index 78ab074ed9..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PublicConstrainedMapGenerator.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -package software.amazon.smithy.rust.codegen.server.smithy.generators - -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.util.expectTrait - -// TODO Docs -// TODO Unit tests -class PublicConstrainedMapGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - val writer: RustWriter, - val shape: MapShape -) { - fun render() { - val lengthTrait = shape.expectTrait() - - val name = symbolProvider.toSymbol(shape).name - val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" - val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) - - val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { - "(${lengthTrait.min.get()}..=${lengthTrait.max.get()}).contains(&length)" - } else if (lengthTrait.min.isPresent) { - "${lengthTrait.min.get()} <= length" - } else { - "length <= ${lengthTrait.max.get()}" - } - - // TODO Docs for everything. - // TODO Use TryFrom from Dan's PR. - writer.rustTemplate( - """ - ##[derive(Debug, Clone, PartialEq)] - pub struct $name(pub(crate) $inner); - - impl $name { - pub fn parse(value: $inner) -> Result { - Self::try_from(value) - } - - pub fn inner(&self) -> &$inner { - &self.0 - } - } - - impl #{ConstrainedTrait} for $name { - type Unconstrained = #{UnconstrainedSymbol}; - } - - impl std::convert::TryFrom<$inner> for $name { - type Error = #{ConstraintViolation}; - - fn try_from(value: $inner) -> Result { - let length = value.len(); - if $condition { - Ok(Self(value)) - } else { - Err(#{ConstraintViolation}::Length(length)) - } - } - } - """, - "KeySymbol" to symbolProvider.toSymbol(model.expectShape(shape.key.target)), - "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "UnconstrainedSymbol" to unconstrainedShapeSymbolProvider.toSymbol(shape), - "ConstraintViolation" to constraintViolation - ) - } -} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index b864608ad8..ffdfd851bd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -24,7 +24,7 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata @@ -54,9 +54,9 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase class ServerBuilderGenerator( private val codegenContext: CodegenContext, private val shape: StructureShape, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider? = null, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider? = null, ) { - private val takeInUnconstrainedTypes = constrainedShapeSymbolProvider != null + private val takeInUnconstrainedTypes = pubCrateConstrainedShapeSymbolProvider != null private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val constraintViolationSymbolProvider = @@ -422,7 +422,7 @@ class ServerBuilderGenerator( val strippedOption = if (memberHasConstraintTraitOrTargetHasConstraintTrait(member)) { symbolProvider.toSymbol(member) } else { - constrainedShapeSymbolProvider!!.toSymbol(member) + pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index cf311129c0..13f1adf550 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -12,7 +12,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -23,7 +23,7 @@ class UnconstrainedCollectionGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: CollectionShape @@ -36,7 +36,7 @@ class UnconstrainedCollectionGenerator( val name = symbol.name val innerShape = model.expectShape(shape.member.target) val innerUnconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) - val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 01a873da36..f5d61dc5b5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -19,7 +19,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -32,7 +32,7 @@ class UnconstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: ConstrainedShapeSymbolProvider, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, val shape: MapShape @@ -45,7 +45,7 @@ class UnconstrainedMapGenerator( private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { symbolProvider.toSymbol(shape) } else { - constrainedShapeSymbolProvider.toSymbol(shape) + pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) } fun render() { @@ -118,7 +118,7 @@ class UnconstrainedMapGenerator( !valueShape.isDirectlyConstrained(symbolProvider) && !valueShape.isStructureShape val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { - constrainedShapeSymbolProvider.toSymbol(valueShape) + pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { symbolProvider.toSymbol(valueShape) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 3231402f5e..da0eb789f1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -13,7 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -71,14 +71,14 @@ class UnconstrainedCollectionGeneratorTest { } val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { - ConstrainedCollectionShape( + ConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, writer, it ).render() @@ -91,7 +91,7 @@ class UnconstrainedCollectionGeneratorTest { model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, it diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index f6a0767888..eab8f11ae2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -13,7 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -73,14 +73,14 @@ class UnconstrainedMapGeneratorTest { } val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(mapA, mapB).forEach { - ConstrainedMapGenerator( + PubCrateConstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, writer, it ).render() @@ -93,7 +93,7 @@ class UnconstrainedMapGeneratorTest { model, symbolProvider, unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, writer, it diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt similarity index 99% rename from codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 7242c29bc1..1c0fa377c9 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -class ConstrainedShapeSymbolProvider( +class PubCrateConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, private val serviceShape: ServiceShape, From c50ca5842f31eb8073ea674a2d337b102b55c4f4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 18:37:42 +0200 Subject: [PATCH 092/255] cleanup and some docs for {PubCrate}ConstrainedShapeSymbolProvider --- .../smithy/ConstrainedShapeSymbolProvider.kt | 20 ++++++- .../server/smithy/ServerCodegenVisitor.kt | 6 +- ...ateConstrainedCollectionShapeGenerator.kt} | 30 ++++++++-- .../PubCrateConstrainedMapGenerator.kt | 40 +++++-------- .../UnconstrainedCollectionGeneratorTest.kt | 2 +- .../smithy/rust/codegen/smithy/Constraints.kt | 36 +++--------- .../PubCrateConstrainedShapeSymbolProvider.kt | 56 ++++++++++++++----- .../smithy/generators/StructureGenerator.kt | 3 +- 8 files changed, 113 insertions(+), 80 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{ConstrainedCollectionShapeGenerator.kt => PubCrateConstrainedCollectionShapeGenerator.kt} (79%) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 2c1c33a047..dec53c8de3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -31,8 +31,26 @@ import software.amazon.smithy.rust.codegen.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase -// TODO Docs. This symbol provider is wrapped by the other ones. // TODO Unit tests. +/** + * The [ConstrainedShapeSymbolProvider] returns, for a given _directly_ + * constrained shape, a symbol whose Rust type can hold the constrained values. + * + * For all shapes with supported traits directly attached to them, this type is + * a [RustType.Opaque] wrapper tuple newtype holding the inner constrained + * type. + * + * The symbols this symbol provider returns are always public and exposed to + * the end user. + * + * This symbol provider is meant to be used "deep" within the wrapped symbol + * providers chain, just above the core base symbol provider, `SymbolVisitor`. + * + * If the shape is _transitively but not directly_ constrained, use + * [PubCrateConstrainedShapeSymbolProvider] instead, which returns symbols + * whose associated types are `pub(crate)` and thus not exposed to the end + * user. + */ class ConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 8e963e5e5e..64e5400768 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -20,7 +20,7 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedCollectionShapeGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionShapeGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator @@ -258,7 +258,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for list $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedCollectionShapeGenerator( + PubCrateConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, @@ -291,7 +291,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for set $shape") rustCrate.withModule(constrainedModule) { writer -> - ConstrainedCollectionShapeGenerator( + PubCrateConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt similarity index 79% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt index 04948ad7e2..854532dc11 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionShapeGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt @@ -17,10 +17,26 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained -// TODO Docs // TODO Unit tests -class ConstrainedCollectionShapeGenerator( +/** + * A generator for a wrapper tuple newtype over a collection shape's symbol + * type. + * + * This newtype is for a collection shape that is _transitively_ constrained, + * but not directly. That is, the collection shape does not have a constraint + * trait attached, but the members it holds reach a constrained shape. The + * generated newtype is therefore `pub(crate)`, as the class name indicates, + * and is not available to end users. After deserialization, upon constraint + * traits' enforcement, this type is converted into the regular `Vec` the user + * sees via the generated converters. + * + * TODO(https://github.com/awslabs/smithy-rs/issues/1401) If the collection + * shape is _directly_ constrained, use [ConstrainedCollectionGenerator] + * instead. + */ +class PubCrateConstrainedCollectionShapeGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, @@ -37,7 +53,11 @@ class ConstrainedCollectionShapeGenerator( val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) - val innerConstrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) + val innerConstrainedSymbol = if (innerShape.isTransitivelyConstrained(model, symbolProvider)) { + pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) + } else { + symbolProvider.toSymbol(innerShape) + } // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, // then its corresponding constrained type is the `pub(crate)` wrapper tuple type, which needs converting into @@ -59,11 +79,11 @@ class ConstrainedCollectionShapeGenerator( """ ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::vec::Vec<#{InnerConstrainedSymbol}>); - + impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; } - + impl From<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { ${ if (innerNeedsConstraining) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 7ab8dd5008..3332423644 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -8,9 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape -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.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility @@ -21,18 +19,22 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained // TODO Unit tests /** * A generator for a wrapper tuple newtype over a map shape's symbol type. * - * This newtype is for a map shape that is _transitively_ constrained, but not directly. That is, the map shape - * does not have a constraint trait attached, but the keys and/or values it holds reach a constrained shape. The - * generated newtype is therefore `pub(crate)`, as the class name indicates, and is not available to end users. After - * deserialization, upon constraint traits' enforcement, this type is converted into the regular `HashMap` the user sees + * This newtype is for a map shape that is _transitively_ constrained, but not + * directly. That is, the map shape does not have a constraint trait attached, + * but the keys and/or values it holds reach a constrained shape. The generated + * newtype is therefore `pub(crate)`, as the class name indicates, and is not + * available to end users. After deserialization, upon constraint traits' + * enforcement, this type is converted into the regular `HashMap` the user sees * via the generated converters. * - * If the map shape is _directly_ constrained, use [ConstrainedMapGenerator] instead. + * If the map shape is _directly_ constrained, use [ConstrainedMapGenerator] + * instead. */ class PubCrateConstrainedMapGenerator( val model: Model, @@ -52,12 +54,8 @@ class PubCrateConstrainedMapGenerator( val name = constrainedSymbol.name val keyShape = model.expectShape(shape.key.target, StringShape::class.java) val valueShape = model.expectShape(shape.value.target) - val keySymbol = if (isKeyConstrained(keyShape)) { - pubCrateConstrainedShapeSymbolProvider.toSymbol(keyShape) - } else { - symbolProvider.toSymbol(keyShape) - } - val valueSymbol = if (isValueConstrained(valueShape)) { + val keySymbol = symbolProvider.toSymbol(keyShape) + val valueSymbol = if (valueShape.isTransitivelyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { symbolProvider.toSymbol(valueShape) @@ -74,11 +72,11 @@ class PubCrateConstrainedMapGenerator( """ ##[derive(Debug, Clone)] pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); - + impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; } - + impl From<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { ${ if (innerNeedsConstraining) { @@ -107,16 +105,4 @@ class PubCrateConstrainedMapGenerator( ) } } - - // TODO These are copied from `UnconstrainedMapGenerator.kt`. - private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) - - private fun isValueConstrained(shape: Shape): Boolean = when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) - is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - is StringShape -> shape.isDirectlyConstrained(symbolProvider) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. - else -> false - } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index da0eb789f1..980e10d855 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -74,7 +74,7 @@ class UnconstrainedCollectionGeneratorTest { val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { - ConstrainedCollectionShapeGenerator( + PubCrateConstrainedCollectionShapeGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 767ee57517..5a32e1f522 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -3,11 +3,8 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker -import software.amazon.smithy.model.shapes.CollectionShape -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.SetShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape @@ -55,31 +52,8 @@ fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (th else -> false } -fun StructureShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - -fun CollectionShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - -fun ListShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) -fun SetShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - (this as CollectionShape).canReachConstrainedShape(model, symbolProvider) - -fun MapShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - -fun MemberShape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - this.isDirectlyConstrained(symbolProvider) || this.targetCanReachConstrainedShape(model, symbolProvider) - -// TODO Callers should use `MemberShape.canReachConstrainedShape`, and this function should be inlined. fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - when (val targetShape = model.expectShape(this.target)) { - is CollectionShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - is StructureShape -> targetShape.canReachConstrainedShape(model, symbolProvider) - else -> targetShape.isDirectlyConstrained(symbolProvider) - } + model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) fun MemberShape.requiresNewtype() = // Note that member shapes whose only constraint trait is `required` do not require a newtype. @@ -87,4 +61,10 @@ fun MemberShape.requiresNewtype() = this.hasTrait() || // `uniqueItems` is deprecated, so we ignore it. // this.hasTrait() || - this.hasTrait() \ No newline at end of file + this.hasTrait() + +fun Shape.isTransitivelyConstrained(model: Model, symbolProvider: SymbolProvider) = + !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) + +fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 1c0fa377c9..f88b2041a4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -14,13 +14,42 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.SimpleShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.util.PANIC import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase +// TODO Unit tests. +/** + * The [PubCrateConstrainedShapeSymbolProvider] returns, for a given + * _transitively but not directly_ constrained shape, a symbol whose Rust type + * can hold the constrained values. + * + * For collection and map shapes, this type is a [RustType.Opaque] wrapper + * tuple newtype holding a container over the inner constrained type. For + * member shapes, it's whatever their target shape resolves to. + * + * The class name is prefixed with `PubCrate` because the symbols it returns + * have associated types that are generated as `pub(crate)`. See the + * `PubCrate*Generator` classes to see how these types are generated. + * + * It is important that this symbol provider _not_ wrap + * [ConstrainedShapeSymbolProvider], since otherwise it will eventually + * delegate to it and generate a symbol with a `pub` type. + * + * Note simple shapes cannot be transitively but not directly constrained, so + * this symbol provider is only implemented for aggregate shapes. The symbol + * provider will intentionally crash in such a case to avoid the caller + * incorrectly using it. + * + * If the shape is _directly_ constrained, use [ConstrainedShapeSymbolProvider] + * instead. + */ class PubCrateConstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, @@ -64,22 +93,16 @@ class PubCrateConstrainedShapeSymbolProvider( } else symbol private fun errorMessage(shape: Shape) = - "This symbol provider was called with $shape. However, it can only be called with a shape that is (transitively) constrained" - - override fun toSymbol(shape: Shape): Symbol = - when (shape) { - is CollectionShape -> { - require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } + "This symbol provider was called with $shape. However, it can only be called with a shape that is transitively constrained" - constrainedSymbolForCollectionOrMapShape(shape) - } - is MapShape -> { - require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } + override fun toSymbol(shape: Shape): Symbol { + require(shape.isTransitivelyConstrained(model, base)) { errorMessage(shape) } + return when (shape) { + is CollectionShape, is MapShape -> { constrainedSymbolForCollectionOrMapShape(shape) } is MemberShape -> { - require(shape.canReachConstrainedShape(model, base)) { errorMessage(shape) } check(model.expectShape(shape.container).isStructureShape) if (shape.requiresNewtype()) { @@ -112,12 +135,17 @@ class PubCrateConstrainedShapeSymbolProvider( } } } - else -> { - // TODO Other shape types. - // TODO We should arguably panic here? Since the rest are either public / don't need constrained type. + is StructureShape, is UnionShape -> { + // Structure shapes and union shapes always generate a [RustType.Opaque] constrained type. base.toSymbol(shape) } + else -> { + check(shape is SimpleShape) + // The rest of the shape types are simple shapes; they generate a public constrained type. + PANIC(errorMessage(shape)) + } } + } } fun constrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 7c4e79cc34..05232a5f1e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -33,6 +33,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.error.ErrorGenerato import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.renamedFrom import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.getTrait @@ -62,7 +63,7 @@ fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: Symb return this.setterName() } - return if (this.canReachConstrainedShape(model, symbolProvider)) { + return if (this.targetCanReachConstrainedShape(model, symbolProvider)) { "set_${this.memberName.toSnakeCase()}" } else { this.memberName.toSnakeCase() From 431717581c5b98ec55e57dfcce86b1dfa66e6a4a Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 19:06:51 +0200 Subject: [PATCH 093/255] general cleanups --- .../smithy/ConstrainedShapeSymbolProvider.kt | 47 +++++------------ .../smithy/rust/codegen/smithy/Constraints.kt | 20 ++++++-- .../PubCrateConstrainedShapeSymbolProvider.kt | 28 ++--------- .../rust/codegen/smithy/SymbolVisitor.kt | 50 +++++++++++-------- .../UnconstrainedShapeSymbolProvider.kt | 33 +++--------- 5 files changed, 68 insertions(+), 110 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index dec53c8de3..e06ff1e563 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -14,18 +14,16 @@ import software.amazon.smithy.model.shapes.MemberShape 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.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.Models -import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.contextName +import software.amazon.smithy.rust.codegen.smithy.handleOptionality +import software.amazon.smithy.rust.codegen.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.locatedIn -import software.amazon.smithy.rust.codegen.smithy.makeOptional -import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.util.hasTrait @@ -68,11 +66,12 @@ class ConstrainedShapeSymbolProvider( override fun toSymbol(shape: Shape): Symbol { return when (shape) { is MemberShape -> { - // TODO member shapes can have constraint traits (constraint trait precedence). + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Member shapes can have constraint traits + // (constraint trait precedence). val target = model.expectShape(shape.target) val targetSymbol = this.toSymbol(target) // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) } is MapShape -> { if (shape.isDirectlyConstrained(base)) { @@ -88,7 +87,8 @@ class ConstrainedShapeSymbolProvider( } } is CollectionShape -> { - // TODO Both arms return the same because we haven't implemented any constraint trait on collection shapes yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Both arms return the same because we haven't + // implemented any constraint trait on collection shapes yet. if (shape.isDirectlyConstrained(base)) { val inner = this.toSymbol(shape.member) symbolBuilder(shape, RustType.Vec(inner.rustType())).addReference(inner).build() @@ -98,37 +98,14 @@ class ConstrainedShapeSymbolProvider( } } is StringShape -> { - if (!shape.isDirectlyConstrained(base)) { - return base.toSymbol(shape) - } - - if (shape.hasTrait()) { - // String shape has a constraint trait in addition to the `enum` trait. - // The `enum` trait takes precedence, so the base symbol provider will generate a Rust `enum`. - // We warn about this case in [ServerCodegenVisitor]. - // See https://github.com/awslabs/smithy/issues/1121f for more information. - return base.toSymbol(shape) + if (shape.isDirectlyConstrained(base)) { + val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) + symbolBuilder(shape, rustType).locatedIn(Models).build() + } else { + base.toSymbol(shape) } - - val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) - symbolBuilder(shape, rustType).locatedIn(Models).build() } else -> base.toSymbol(shape) } } - - // TODO The following two methods have been copied from `SymbolVisitor.kt` - private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = - if (shape.hasTrait()) { - symbol.makeRustBoxed() - } else symbol - - private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = - if (member.isRequired) { - symbol - } else if (nullableIndex.isNullable(member)) { - symbol.makeOptional() - } else { - symbol - } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 5a32e1f522..c467180f04 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -34,13 +34,16 @@ import software.amazon.smithy.rust.codegen.util.hasTrait fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The only reason why the functions in this file have - // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as - // `required`, so we can't use `member.isOptional` here. + // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } is MapShape -> this.hasTrait() - // TODO While `enum` traits are constraint traits, we're outright rejecting unknown enum variants as deserialization - // errors instead of parsing them into an unconstrained type that we then constrain after parsing the entire request. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) While `enum` traits are constraint traits, we're outright + // rejecting unknown enum variants as deserialization errors instead of parsing them into an unconstrained type + // that we then constrain after parsing the entire request. If the string shape has both the `enum` trait and + // another constraint trait, we warn about this case in [ServerCodegenVisitor]. + // See also https://github.com/awslabs/smithy/issues/1121. is StringShape -> !this.hasTrait() && this.hasTrait() else -> false } @@ -67,4 +70,11 @@ fun Shape.isTransitivelyConstrained(model: Model, symbolProvider: SymbolProvider !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } \ No newline at end of file + if (this is MemberShape) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented + // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, + // so we can't simply delegate to the `else` branch when we implement them. + this.targetCanReachConstrainedShape(model, symbolProvider) + } else { + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } + } \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index f88b2041a4..b39d8da3cf 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -71,27 +71,6 @@ class PubCrateConstrainedShapeSymbolProvider( .build() } - // TODO The following two methods have been copied from `SymbolVisitor.kt`. - private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = - if (member.isRequired) { - symbol - } else if (nullableIndex.isNullable(member)) { - symbol.makeOptional() - } else { - symbol - } - - /** - * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with - * [RustBoxTrait]; otherwise returns [symbol] unchanged. - * - * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. - */ - private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = - if (shape.hasTrait()) { - symbol.makeRustBoxed() - } else symbol - private fun errorMessage(shape: Shape) = "This symbol provider was called with $shape. However, it can only be called with a shape that is transitively constrained" @@ -117,7 +96,7 @@ class PubCrateConstrainedShapeSymbolProvider( } else { val targetSymbol = this.toSymbol(targetShape) // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) } } else { val targetShape = model.expectShape(shape.target) @@ -131,7 +110,7 @@ class PubCrateConstrainedShapeSymbolProvider( } else { val targetSymbol = this.toSymbol(targetShape) // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) } } } @@ -141,7 +120,8 @@ class PubCrateConstrainedShapeSymbolProvider( } else -> { check(shape is SimpleShape) - // The rest of the shape types are simple shapes; they generate a public constrained type. + // The rest of the shape types are simple shapes, which are impossible to be transitively but not + // directly constrained; directly constrained shapes generate public constrained types. PANIC(errorMessage(shape)) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index ce82174745..3326bf5b15 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -242,26 +242,6 @@ class SymbolVisitor( return RuntimeType.Blob(config.runtimeConfig).toSymbol() } - private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = - if (config.handleRequired && member.isRequired) { - symbol - } else if (nullableIndex.isNullable(member)) { - symbol.makeOptional() - } else { - symbol - } - - /** - * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with - * [RustBoxTrait]; otherwise returns [symbol] unchanged. - * - * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. - */ - private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = - if (shape.hasTrait()) { - symbol.makeRustBoxed() - } else symbol - private fun simpleShape(shape: SimpleShape): Symbol { return symbolBuilder(shape, SimpleShapes.getValue(shape::class)).setDefault(Default.RustDefault).build() } @@ -369,7 +349,7 @@ class SymbolVisitor( return targetSymbol.letIf(config.handleRustBoxing) { handleRustBoxing(it, shape) }.let { - handleOptionality(it, shape) + handleOptionality(it, shape, nullableIndex) } } @@ -378,6 +358,34 @@ class SymbolVisitor( } } +fun handleOptionality( + symbol: Symbol, + member: MemberShape, + nullableIndex: NullableIndex, + config: SymbolVisitorConfig? = null +): Symbol { + val handleRequired = config?.handleRequired ?: true + return if (handleRequired && member.isRequired) { + symbol + } else if (nullableIndex.isNullable(member)) { + symbol.makeOptional() + } else { + symbol + } +} + +/** + * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with + * [RustBoxTrait]; otherwise returns [symbol] unchanged. + * + * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. + */ +fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = + if (shape.hasTrait()) { + symbol.makeRustBoxed() + } else symbol + + fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder { val builder = Symbol.builder().putProperty(SHAPE_KEY, shape) return builder.rustType(rustType) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index dc0f8e9d40..d9747a98c5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -86,27 +85,6 @@ class UnconstrainedShapeSymbolProvider( .build() } - // TODO The following two methods have been copied from `SymbolVisitor.kt`. - private fun handleOptionality(symbol: Symbol, member: MemberShape): Symbol = - if (member.isRequired) { - symbol - } else if (nullableIndex.isNullable(member)) { - symbol.makeOptional() - } else { - symbol - } - - /** - * Boxes and returns [symbol], the symbol for the target of the member shape [shape], if [shape] is annotated with - * [RustBoxTrait]; otherwise returns [symbol] unchanged. - * - * See `RecursiveShapeBoxer.kt` for the model transformation pass that annotates model shapes with [RustBoxTrait]. - */ - private fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = - if (shape.hasTrait()) { - symbol.makeRustBoxed() - } else symbol - override fun toSymbol(shape: Shape): Symbol = when (shape) { is CollectionShape -> { @@ -137,15 +115,20 @@ class UnconstrainedShapeSymbolProvider( // trait targeting a map shape of string keys and values; or // * how [ServerHttpBoundProtocolGenerator] deserializes for a member shape with the `httpQuery` trait // targeting a collection shape that can reach a constrained shape. - if (model.expectShape(shape.container).isStructureShape && shape.targetCanReachConstrainedShape(model, base)) { + if (model.expectShape(shape.container).isStructureShape && shape.targetCanReachConstrainedShape( + model, + base + ) + ) { val targetShape = model.expectShape(shape.target) val targetSymbol = this.toSymbol(targetShape) // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape) + handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) } else { base.toSymbol(shape) } - // TODO Constraint traits on member shapes are not implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not + // implemented yet. } else -> base.toSymbol(shape) } From 0a0266160ca8cbccda05dd4fa4696440df452931 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 20 May 2022 23:49:09 +0200 Subject: [PATCH 094/255] fix SymbolVisitor: send config --- .../software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 3326bf5b15..1b07b28780 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -349,7 +349,7 @@ class SymbolVisitor( return targetSymbol.letIf(config.handleRustBoxing) { handleRustBoxing(it, shape) }.let { - handleOptionality(it, shape, nullableIndex) + handleOptionality(it, shape, nullableIndex, config) } } From af2dda10397774d555d92d8d8e81cdef2430eab8 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 13:56:49 +0200 Subject: [PATCH 095/255] Cleanup member shape impl of PubCrateConstrainedShapeSymbolProvider --- .../generators/ServerBuilderGenerator.kt | 31 ++++++++----- .../smithy/rust/codegen/smithy/Constraints.kt | 3 ++ .../PubCrateConstrainedShapeSymbolProvider.kt | 45 +++++++------------ 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ffdfd851bd..c4606d8803 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed @@ -238,12 +239,10 @@ class ServerBuilderGenerator( conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape || - memberHasConstraintTraitOrTargetHasConstraintTrait(member) // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" - if (!constrainedTypeHoldsFinalType) varExpr = "($varExpr).into()" + if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { rust("$maybeConstrainedConstrained($varExpr)") @@ -258,6 +257,21 @@ class ServerBuilderGenerator( } } + /** + * Returns whether the constrained builder member type (the type on which the `Constrained` trait is implemented) + * is the final type the user sees when receiving the built struct. This is true when the corresponding constrained + * type is public and not `pub(crate)`, which happens when the target is a structure or is directly constrained. + * + * An example where this returns false is when the member shape targets a list whose members are lists of structures + * having at least one `required` member. In this case the member shape is transitively but not directly constrained, + * so the generated constrained type is `pub(crate)` and needs converting into the final type the user will be + * exposed to. + * + * See [PubCrateConstrainedShapeSymbolProvider] too. + */ + fun constrainedTypeHoldsFinalType(member: MemberShape) = + model.expectShape(member.target).isStructureShape || member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider) + /** * Render a `set_foo` method. * This method is able to take in unconstrained types for constrained shapes, like builders of structs in the case @@ -410,16 +424,13 @@ class ServerBuilderGenerator( ) } - private fun memberHasConstraintTraitOrTargetHasConstraintTrait(member: MemberShape) = - member.isDirectlyConstrained(symbolProvider) || (model.expectShape(member.target).isDirectlyConstrained(symbolProvider)) - /** * Returns the symbol for a builder's member. * All builder members are optional, but only some are `Option`s where `T` needs to be constrained. */ private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { - val strippedOption = if (memberHasConstraintTraitOrTargetHasConstraintTrait(member)) { + val strippedOption = if (member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider)) { symbolProvider.toSymbol(member) } else { pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) @@ -465,8 +476,6 @@ class ServerBuilderGenerator( writer.rustBlock("#T", structureSymbol) { for (member in members) { val memberName = symbolProvider.toMemberName(member) - val constrainedTypeHoldsFinalType = model.expectShape(member.target).isStructureShape - || memberHasConstraintTraitOrTargetHasConstraintTrait(member) withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). @@ -483,7 +492,7 @@ class ServerBuilderGenerator( #{MaybeConstrained}::Unconstrained(x) => Ok(Box::new(x.try_into()?)), }) .map(|res| - res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } + res${ if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())" } .map_err(|err| ConstraintViolation::${constraintViolation.name()}(Box::new(err))) ) .transpose()? @@ -498,7 +507,7 @@ class ServerBuilderGenerator( #{MaybeConstrained}::Unconstrained(x) => x.try_into(), }) .map(|res| - res${ if (constrainedTypeHoldsFinalType) "" else ".map(|v| v.into())" } + res${ if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())" } .map_err(ConstraintViolation::${constraintViolation.name()}) ) .transpose()? diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index c467180f04..1b5b030032 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -66,6 +66,9 @@ fun MemberShape.requiresNewtype() = // this.hasTrait() || this.hasTrait() +fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider) = + this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) + fun Shape.isTransitivelyConstrained(model: Model, symbolProvider: SymbolProvider) = !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index b39d8da3cf..23391e33f5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -16,11 +16,9 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.SimpleShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.util.PANIC -import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -47,6 +45,10 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase * provider will intentionally crash in such a case to avoid the caller * incorrectly using it. * + * Note also that for the purposes of this symbol provider, a member shape is + * transitively but not directly constrained only in the case where it itself + * is not directly constrained and its target also is not directly constrained. + * * If the shape is _directly_ constrained, use [ConstrainedShapeSymbolProvider] * instead. */ @@ -72,7 +74,7 @@ class PubCrateConstrainedShapeSymbolProvider( } private fun errorMessage(shape: Shape) = - "This symbol provider was called with $shape. However, it can only be called with a shape that is transitively constrained" + "This symbol provider was called with $shape. However, it can only be called with a shape that is transitively constrained." override fun toSymbol(shape: Shape): Symbol { require(shape.isTransitivelyConstrained(model, base)) { errorMessage(shape) } @@ -82,36 +84,19 @@ class PubCrateConstrainedShapeSymbolProvider( constrainedSymbolForCollectionOrMapShape(shape) } is MemberShape -> { - check(model.expectShape(shape.container).isStructureShape) - - if (shape.requiresNewtype()) { - //TODO() + require(model.expectShape(shape.container).isStructureShape) { + "This arm is only exercised by `ServerBuilderGenerator`" + } + require(!shape.hasConstraintTraitOrTargetHasConstraintTrait(model, base)) { errorMessage(shape) } - // TODO What follows is wrong; here we should refer to an opaque type for the member shape. - // But for now we add this to not make the validation model crash. + val targetShape = model.expectShape(shape.target) - val targetShape = model.expectShape(shape.target) - if (targetShape is SimpleShape) { - base.toSymbol(shape) - } else { - val targetSymbol = this.toSymbol(targetShape) - // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) - } + if (targetShape is SimpleShape) { + base.toSymbol(shape) } else { - val targetShape = model.expectShape(shape.target) - - if (targetShape is SimpleShape) { - check(shape.hasTrait()) { - "Targeting a simple shape that can reach a constrained shape and does not need a newtype; the member shape must be `required`" - } - - base.toSymbol(shape) - } else { - val targetSymbol = this.toSymbol(targetShape) - // Handle boxing first so we end up with `Option>`, not `Box>`. - handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) - } + val targetSymbol = this.toSymbol(targetShape) + // Handle boxing first so we end up with `Option>`, not `Box>`. + handleOptionality(handleRustBoxing(targetSymbol, shape), shape, nullableIndex) } } is StructureShape, is UnionShape -> { From 6e458ea5f5815d85241afe26a5b7b9af5939e8a7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 17:01:19 +0200 Subject: [PATCH 096/255] Move ConstraintViolations to model module --- .../ConstraintViolationSymbolProvider.kt | 17 +++-- .../server/smithy/ServerCodegenVisitor.kt | 72 +++++++++++-------- .../generators/ConstrainedMapGenerator.kt | 1 + .../UnconstrainedCollectionGenerator.kt | 28 +++++--- .../generators/UnconstrainedMapGenerator.kt | 66 ++++++++++------- 5 files changed, 114 insertions(+), 70 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index e39e5aba43..f4314c6185 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -24,10 +24,10 @@ import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.unconstrainedTypeNameForCollectionOrMapShape import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Unit tests. +// TODO Docs. class ConstraintViolationSymbolProvider( private val base: RustSymbolProvider, private val model: Model, @@ -38,21 +38,26 @@ class ConstraintViolationSymbolProvider( private fun constraintViolationSymbolForCollectionOrMapShape(shape: Shape): Symbol { check(shape is CollectionShape || shape is MapShape) - // TODO Move ConstraintViolation type to the constrained namespace. - val unconstrainedTypeName = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) - val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(unconstrainedTypeName.toSnakeCase())}" - val rustType = RustType.Opaque(constraintViolationName, namespace) + val symbol = base.toSymbol(shape) + val constraintViolationNamespace = + "${symbol.namespace.let { it.ifEmpty { "crate::${Models.namespace}" } }}::${ + RustReservedWords.escapeIfNeeded( + shape.contextName(serviceShape).toSnakeCase() + ) + }" + val rustType = RustType.Opaque(constraintViolationName, constraintViolationNamespace) return Symbol.builder() .rustType(rustType) .name(rustType.name) .namespace(rustType.namespace, "::") - .definitionFile(Unconstrained.filename) + .definitionFile(symbol.definitionFile) .build() } override fun toSymbol(shape: Shape): Symbol = when (shape) { is CollectionShape -> { + // TODO Move these checks out. check(shape.canReachConstrainedShape(model, base)) constraintViolationSymbolForCollectionOrMapShape(shape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 64e5400768..98af212826 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -20,9 +20,9 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionShapeGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionShapeGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator @@ -237,6 +237,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } + // TODO Have a single method for collections. override fun listShape(shape: ListShape) { if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( model, @@ -244,16 +245,19 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") - rustCrate.withModule(unconstrainedModule) { writer -> - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - writer, - shape - ).render() + rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + shape + ).render() + } } logger.info("[rust-server-codegen] Generating a constrained type for list $shape") @@ -277,16 +281,19 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for set $shape") - rustCrate.withModule(unconstrainedModule) { writer -> - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - writer, - shape - ).render() + rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + shape + ).render() + } } logger.info("[rust-server-codegen] Generating a constrained type for set $shape") @@ -323,16 +330,19 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") - rustCrate.withModule(unconstrainedModule) { writer -> - UnconstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - writer, - shape - ).render() + rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> + UnconstrainedMapGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + shape + ).render() + } } if (!shape.isDirectlyConstrained(symbolProvider)) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index bb3a39f6c8..9963342417 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -31,6 +31,7 @@ class ConstrainedMapGenerator( val name = symbolProvider.toSymbol(shape).name val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" + // TODO This won't work if the map is only used in operation output, because we don't render the constraint violation symbol. val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 13f1adf550..70cd2d7d1f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -25,7 +25,8 @@ class UnconstrainedCollectionGenerator( private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - val writer: RustWriter, + private val unconstrainedModuleWriter: RustWriter, + private val modelsModuleWriter: RustWriter, val shape: CollectionShape ) { fun render() { @@ -37,11 +38,12 @@ class UnconstrainedCollectionGenerator( val innerShape = model.expectShape(shape.member.target) val innerUnconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) - val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name + val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) + val constraintViolationName = constraintViolationSymbol.name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) // TODO Don't be lazy and don't use `Result<_, >`. - writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ ##[derive(Debug, Clone)] @@ -53,11 +55,8 @@ class UnconstrainedCollectionGenerator( } } - ##[derive(Debug, PartialEq)] - pub struct $constraintViolationName(pub(crate) #{InnerConstraintViolationSymbol}); - impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { - type Error = $constraintViolationName; + type Error = #{ConstraintViolationSymbol}; fn try_from(value: $name) -> Result { let res: Result<_, #{InnerConstraintViolationSymbol}> = value @@ -66,15 +65,28 @@ class UnconstrainedCollectionGenerator( .map(|inner| inner.try_into()) .collect(); res.map(Self) - .map_err(ConstraintViolation) + .map_err(#{ConstraintViolationSymbol}) } } """, "InnerUnconstrainedSymbol" to innerUnconstrainedSymbol, "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, "ConstrainedSymbol" to constrainedSymbol, + "ConstraintViolationSymbol" to constraintViolationSymbol, "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), ) } + + modelsModuleWriter.withModule( + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + ) { + rustTemplate( + """ + ##[derive(Debug, PartialEq)] + pub struct $constraintViolationName(pub(crate) #{InnerConstraintViolationSymbol}); + """, + "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index f5d61dc5b5..c8979f6508 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -34,12 +34,14 @@ class UnconstrainedMapGenerator( private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - val writer: RustWriter, + private val unconstrainedModuleWriter: RustWriter, + private val modelsModuleWriter: RustWriter, val shape: MapShape ) { private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) private val name = symbol.name - private val constraintViolationName = constraintViolationSymbolProvider.toSymbol(shape).name + private val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) + private val constraintViolationName = constraintViolationSymbol.name private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) private val valueShape = model.expectShape(shape.value.target) private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { @@ -63,6 +65,34 @@ class UnconstrainedMapGenerator( } else { symbolProvider.toSymbol(valueShape) } + + unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); + + impl From<$name> for #{MaybeConstrained} { + fn from(value: $name) -> Self { + Self::Unconstrained(value) + } + } + + """, + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + ) + + renderTryFromUnconstrainedForConstrained(this) + } + + renderConstraintViolation() + } + + // TODO Docs for ConstraintViolation. + // TODO KeyConstraintViolation and ValueConstraintViolation need to be `#[doc(hidden)]`. + private fun renderConstraintViolation() { val constraintViolationCodegenScope = listOfNotNull( if (isKeyConstrained(keyShape)) { "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) @@ -76,47 +106,33 @@ class UnconstrainedMapGenerator( }, ).toTypedArray() - // TODO Docs for ConstraintViolation. - // TODO KeyConstraintViolation and ValueConstraintViolation need to be `#[doc(hidden)]`. - writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + modelsModuleWriter.withModule( + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + ) { rustTemplate( """ - ##[derive(Debug, Clone)] - pub(crate) struct $name(pub(crate) std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>); - - impl From<$name> for #{MaybeConstrained} { - fn from(value: $name) -> Self { - Self::Unconstrained(value) - } - } - ##[derive(Debug, PartialEq)] pub enum $constraintViolationName { - ${ if (shape.hasTrait()) "Length(usize)," else "" } - ${ if (isKeyConstrained(keyShape)) "Key(#{KeyConstraintViolationSymbol})," else "" } - ${ if (isValueConstrained(valueShape)) "Value(#{ValueConstraintViolationSymbol})," else "" } + ${if (shape.hasTrait()) "Length(usize)," else ""} + ${if (isKeyConstrained(keyShape)) "Key(#{KeyConstraintViolationSymbol})," else ""} + ${if (isValueConstrained(valueShape)) "Value(#{ValueConstraintViolationSymbol})," else ""} } """, - "KeySymbol" to keySymbol, - "ValueSymbol" to valueSymbol, *constraintViolationCodegenScope, - "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), ) - - renderTryFromUnconstrainedForConstrained(this) } } private fun renderTryFromUnconstrainedForConstrained(writer: RustWriter) { writer.rustBlock("impl std::convert::TryFrom<$name> for #{T}", constrainedSymbol) { - rust("type Error = $constraintViolationName;") + rust("type Error = #T;", constraintViolationSymbol) rustBlock("fn try_from(value: $name) -> Result") { if (isKeyConstrained(keyShape) || isValueConstrained(valueShape)) { val resolveToNonPublicConstrainedValueType = isValueConstrained(valueShape) && - !valueShape.isDirectlyConstrained(symbolProvider) && - !valueShape.isStructureShape + !valueShape.isDirectlyConstrained(symbolProvider) && + !valueShape.isStructureShape val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { From 8afed5edde551973db95766bc8005b8f52faa948 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 17:25:44 +0200 Subject: [PATCH 097/255] Clean up ConstraintViolationSymbolProvider --- .../ConstraintViolationSymbolProvider.kt | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index f4314c6185..9fc6de52da 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -22,12 +22,43 @@ import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.toSnakeCase // TODO Unit tests. -// TODO Docs. +/** + * The [ConstraintViolationSymbolProvider] returns, for a given constrained + * shape, a symbol whose Rust type can hold information about constraint + * violations that may occur when building the shape from unconstrained values. + * + * So, for example, given the model: + * + * ```smithy + * @pattern("\\w+") + * @length(min: 1, max: 69) + * string NiceString + * + * structure Structure { + * @required + * niceString: NiceString + * } + * ``` + * + * A `NiceString` built from an arbitrary Rust `String` may give rise to at + * most two constraint trait violations: one for `pattern`, one for `length`. + * Similarly, the shape `Structure` can fail to be built when a value for + * `niceString` is not provided. + * + * Said type is always called `ConstraintViolation`, and resides in a bespoke + * module inside the same module as the _public_ constrained type the user is + * exposed to. When the user is _not_ exposed to the constrained type, the + * constraint violation type's module is a child of the `model` module. + * + * It is the responsibility of the caller to ensure that the shape is + * constrained (either directly or transitively) before using this symbol + * provider. This symbol provider intentionally crashes if the shape is not + * constrained. + */ class ConstraintViolationSymbolProvider( private val base: RustSymbolProvider, private val model: Model, @@ -54,22 +85,14 @@ class ConstraintViolationSymbolProvider( .build() } - override fun toSymbol(shape: Shape): Symbol = - when (shape) { - is CollectionShape -> { - // TODO Move these checks out. - check(shape.canReachConstrainedShape(model, base)) - - constraintViolationSymbolForCollectionOrMapShape(shape) - } - is MapShape -> { - check(shape.canReachConstrainedShape(model, base)) + override fun toSymbol(shape: Shape): Symbol { + check(shape.canReachConstrainedShape(model, base)) + return when (shape) { + is MapShape, is CollectionShape -> { constraintViolationSymbolForCollectionOrMapShape(shape) } is StructureShape -> { - check(shape.canReachConstrainedShape(model, base)) - val builderSymbol = shape.builderSymbol(base) val namespace = builderSymbol.namespace @@ -82,9 +105,11 @@ class ConstraintViolationSymbolProvider( .build() } is StringShape -> { - check(shape.isDirectlyConstrained(base)) - - val namespace = "crate::${Models.namespace}::${RustReservedWords.escapeIfNeeded(shape.contextName(serviceShape).toSnakeCase())}" + val namespace = "crate::${Models.namespace}::${ + RustReservedWords.escapeIfNeeded( + shape.contextName(serviceShape).toSnakeCase() + ) + }" val rustType = RustType.Opaque(constraintViolationName, namespace) Symbol.builder() .rustType(rustType) @@ -93,7 +118,7 @@ class ConstraintViolationSymbolProvider( .definitionFile(Models.filename) .build() } - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other simple shapes can have constraint traits. - else -> base.toSymbol(shape) + else -> TODO("Constraint traits on other shapes not implemented yet") } + } } From 1168c5b617a1b40c419ac4ec50721dad138fdfe9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 17:29:41 +0200 Subject: [PATCH 098/255] Have a single collection visitor method in ServerCodegenVisitor --- .../server/smithy/ServerCodegenVisitor.kt | 45 +++---------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 98af212826..1b442033c0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape @@ -237,50 +238,16 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } - // TODO Have a single method for collections. - override fun listShape(shape: ListShape) { - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( - model, - symbolProvider - ) - ) { - logger.info("[rust-server-codegen] Generating an unconstrained type for list $shape") - rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> - rustCrate.withModule(ModelsModule) { modelsModuleWriter -> - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - shape - ).render() - } - } - - logger.info("[rust-server-codegen] Generating a constrained type for list $shape") - rustCrate.withModule(constrainedModule) { writer -> - PubCrateConstrainedCollectionShapeGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - writer, - shape - ).render() - } - } - } + override fun listShape(shape: ListShape) = collectionShape(shape) + override fun setShape(shape: SetShape) = collectionShape(shape) - override fun setShape(shape: SetShape) { + private fun collectionShape(shape: CollectionShape) { if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( model, symbolProvider ) ) { - logger.info("[rust-server-codegen] Generating an unconstrained type for set $shape") + logger.info("[rust-server-codegen] Generating an unconstrained type for collection shape $shape") rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedCollectionGenerator( @@ -296,7 +263,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } - logger.info("[rust-server-codegen] Generating a constrained type for set $shape") + logger.info("[rust-server-codegen] Generating a constrained type for collection shape $shape") rustCrate.withModule(constrainedModule) { writer -> PubCrateConstrainedCollectionShapeGenerator( model, From 4abc947b783cb8f03a681734773696216d00d006 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 18:09:47 +0200 Subject: [PATCH 099/255] Add some docs to the constrained type generators and their generated types --- .../generators/ConstrainedMapGenerator.kt | 21 ++++++++++++++-- .../ConstrainedShapeGeneratorCommon.kt | 24 +++++++++++++++++++ .../generators/ConstrainedStringGenerator.kt | 20 +++++++++++++--- 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 9963342417..7a8cc52a07 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -16,8 +17,16 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.util.expectTrait -// TODO Docs // TODO Unit tests +/** + * [ConstrainedMapGenerator] generates a wrapper tuple newtype holding a constrained `std::collections::HashMap`. + * This type can be built from unconstrained values, yielding a `ConstraintViolation` when the input does not satisfy + * the constraints. + * + * The [`length` trait] is the only constraint trait applicable to map shapes. + * + * [`length` trait]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait + */ class ConstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, @@ -27,6 +36,7 @@ class ConstrainedMapGenerator( val shape: MapShape ) { fun render() { + // The `length` trait is the only constraint trait applicable to map shapes. val lengthTrait = shape.expectTrait() val name = symbolProvider.toSymbol(shape).name @@ -42,21 +52,28 @@ class ConstrainedMapGenerator( "length <= ${lengthTrait.max.get()}" } - // TODO Docs for everything. // TODO Use TryFrom from Dan's PR. + writer.documentShape(shape, model, note = rustDocsNote(name)) writer.rustTemplate( """ ##[derive(Debug, Clone, PartialEq)] pub struct $name(pub(crate) $inner); impl $name { + /// ${rustDocsParseMethod(name, inner)} pub fn parse(value: $inner) -> Result { Self::try_from(value) } + /// ${rustDocsInnerMethod(inner)} pub fn inner(&self) -> &$inner { &self.0 } + + /// ${rustDocsIntoInnerMethod(inner)} + pub fn into_inner(self) -> $inner { + self.0 + } } impl #{ConstrainedTrait} for $name { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt new file mode 100644 index 0000000000..cbeade5013 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +/** + * Functions shared amongst the constrained shape generators, to keep the DRY and consistent. + */ + +fun rustDocsNote(typeName: String) = + "this is a constrained type because its corresponding modeled Smithy shape has one or more " + + "[constraint traits]. Use [`parse`] or [`$typeName::TryFrom`] to construct values of this type." + + "[constraint traits]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html" + +fun rustDocsParseMethod(typeName: String, inner: String) = + "Constructs a `$typeName` from an [`$inner`], failing when the provided value does not satisfy the modeled constraints." + +fun rustDocsInnerMethod(inner: String) = + "Returns an immutable reference to the underlying [`$inner`]." + +fun rustDocsIntoInnerMethod(inner: String) = + "Consumes the value, returning the underlying [`$inner`]." diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 5e7e8e0f19..07ce811a55 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider @@ -19,8 +20,12 @@ import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.toSnakeCase -// TODO Docs // TODO Unit tests +/** + * [ConstrainedStringGenerator] generates a wrapper tuple newtype holding a constrained `String`. + * This type can be built from unconstrained values, yielding a `ConstraintViolation` when the input does not satisfy + * the constraints. + */ class ConstrainedStringGenerator( val model: Model, val symbolProvider: RustSymbolProvider, @@ -44,25 +49,34 @@ class ConstrainedStringGenerator( "length <= ${lengthTrait.max.get()}" } - // TODO Docs for everything. - // TODO Display impl. + // TODO Docs for `ConstraintViolation`. + // TODO Display impl does not honor `sensitive` trait. + // TODO Use Display from Dan's PR. // TODO Use TryFrom from Dan's PR. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait + writer.documentShape(shape, model, note = rustDocsNote(name)) writer.rustTemplate( """ ##[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $name(pub(crate) $inner); impl $name { + /// ${rustDocsParseMethod(name, inner)} pub fn parse(value: $inner) -> Result { Self::try_from(value) } + /// ${rustDocsInnerMethod(inner)} pub fn inner(&self) -> &$inner { &self.0 } + + /// ${rustDocsIntoInnerMethod(inner)} + pub fn into_inner(self) -> $inner { + self.0 + } } impl #{ConstrainedTrait} for $name { From 3874c0e049842e62d288245a5394e6c92f9f116a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 18:28:05 +0200 Subject: [PATCH 100/255] SIMPLE SIMPLE SIMPLE MODEL --- codegen-server-test/model/simple.smithy | 122 ++---------------------- 1 file changed, 7 insertions(+), 115 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index d4420787fe..e436f4fc35 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -3,129 +3,21 @@ $version: "1.0" namespace com.amazonaws.simple use aws.protocols#restJson1 -use smithy.test#httpRequestTests -use smithy.test#httpResponseTests @restJson1 @title("SimpleService") -@documentation("A simple service example, with a Service resource that can be registered and a readonly healthcheck") service SimpleService { - version: "2022-01-01", - resources: [ - Service, - ], operations: [ - Healthcheck, - StoreServiceBlob, + AnOperation, ], } -@documentation("Id of the service that will be registered") -string ServiceId - -@documentation("Name of the service that will be registered") -string ServiceName - -@error("client") -@documentation( - """ - Returned when a new resource cannot be created because one already exists. - """ -) -structure ResourceAlreadyExists { - @required - message: String -} - -@documentation("A resource that can register services") -resource Service { - identifiers: { id: ServiceId }, - put: RegisterService, -} - -@idempotent -@http(method: "PUT", uri: "/service/{id}") -@documentation("Service register operation") -@httpRequestTests([ - { - id: "RegisterServiceRequestTest", - protocol: "aws.protocols#restJson1", - uri: "/service/1", - headers: { - "Content-Type": "application/json", - }, - params: { id: "1", name: "TestService" }, - body: "{\"name\":\"TestService\"}", - method: "PUT", - } -]) -@httpResponseTests([ - { - id: "RegisterServiceResponseTest", - protocol: "aws.protocols#restJson1", - params: { id: "1", name: "TestService" }, - body: "{\"id\":\"1\",\"name\":\"TestService\"}", - code: 200, - } -]) -operation RegisterService { - input: RegisterServiceInputRequest, - output: RegisterServiceOutputResponse, - errors: [ResourceAlreadyExists] -} - -@documentation("Service register input structure") -structure RegisterServiceInputRequest { - @required - @httpLabel - id: ServiceId, - name: ServiceName, -} - -@documentation("Service register output structure") -structure RegisterServiceOutputResponse { - @required - id: ServiceId, - name: ServiceName, -} - -@readonly -@http(uri: "/healthcheck", method: "GET") -@documentation("Read-only healthcheck operation") -operation Healthcheck { - input: HealthcheckInputRequest, - output: HealthcheckOutputResponse -} - -@documentation("Service healthcheck output structure") -structure HealthcheckInputRequest { - -} - -@documentation("Service healthcheck input structure") -structure HealthcheckOutputResponse { - -} - -@readonly -@http(method: "GET", uri: "/service/{id}/blob") -@documentation("Stores a blob for a service id") -operation StoreServiceBlob { - input: StoreServiceBlobInput, - output: StoreServiceBlobOutput +@http(uri: "/operation", method: "GET") +operation AnOperation { + input: AnOperationInputOutput, + output: AnOperationInputOutput, } -@documentation("Store a blob for a service id input structure") -structure StoreServiceBlobInput { - @required - @httpLabel - id: ServiceId, - @required - @httpPayload - content: Blob, -} - -@documentation("Store a blob for a service id output structure") -structure StoreServiceBlobOutput { - +structure AnOperationInputOutput { + int: Integer } From 783d38432f84a93e01fb23ed4c0a990c638024d0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 18:51:13 +0200 Subject: [PATCH 101/255] Remove some stale TODOs --- .../smithy/generators/ServerBuilderGenerator.kt | 1 - .../codegen/smithy/generators/BuilderGenerator.kt | 1 - .../codegen/smithy/generators/StructureGenerator.kt | 2 -- .../smithy/generators/http/HttpBindingGenerator.kt | 11 ++++++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index c4606d8803..b7b8e3e54c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -484,7 +484,6 @@ class ServerBuilderGenerator( .mapRustType { it.stripOuter() } .isRustBoxed() if (hasBox) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1332) `TryInto` is in Rust 2021's prelude. rustTemplate( """ .map(|v| match *v { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index 74187fe5db..6ad1534904 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -244,7 +244,6 @@ class BuilderGenerator( * ``` */ private fun coreBuilder(writer: RustWriter) { - // TODO Refactor like server writer.rustBlock("#T", structureSymbol) { members.forEach { member -> val memberName = symbolProvider.toMemberName(member) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 05232a5f1e..042586e125 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -105,11 +105,9 @@ class StructureGenerator( .map { symbolProvider.toSymbol(it) }.any { // If any members are not optional && we can't use a default, we need to // generate a fallible builder. - // TODO Won't canUseDefault suffice? !it.isOptional() && !it.canUseDefault() } - // TODO Not quite right. @box not taken into account. Also shape builders / constrained shapes /** * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a * fallible builder to be constructed. diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 855cb2587a..69dfdfc363 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -592,11 +592,12 @@ class HttpBindingGenerator( val func = writer.format(RuntimeType.Base64Encode(runtimeConfig)) "$func(&$targetName)" } else { - // TODO Constraint traits on member traits are not supported yet. Note that here, counterintuitively, - // in case we're rendering a header for a collection, `member` is still referring to the structure - // member shape where the `httpHeader` or `httpPrefixHeaders` trait was found, and _not_ to the - // collection member shape on which we'd have to check for constraint trait precedence. So - // `member.hasPublicConstrainedWrapperTupleType()` is _not_ what we want. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member traits are not + // supported yet. Note that here, counterintuitively, in case we're rendering a header for a + // collection, `member` is still referring to the structure member shape where the `httpHeader` or + // `httpPrefixHeaders` trait was found, and _not_ to the collection member shape on which we'd have + // to check for constraint trait precedence. So `member.hasPublicConstrainedWrapperTupleType()` is + // _not_ what we want. val workingWithPublicConstrainedWrapperTupleType = mode == CodegenMode.Server && target.hasPublicConstrainedWrapperTupleType(model) quoteValue("AsRef::::as_ref(${ if (workingWithPublicConstrainedWrapperTupleType) "&$targetName.0" else targetName })") From 498a0d5ac326938b4e9422f70a038f977b48a26d Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 23 May 2022 18:53:25 +0200 Subject: [PATCH 102/255] Revert "SIMPLE SIMPLE SIMPLE MODEL" This reverts commit 3874c0e049842e62d288245a5394e6c92f9f116a. --- codegen-server-test/model/simple.smithy | 122 ++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 7 deletions(-) diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index e436f4fc35..d4420787fe 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -3,21 +3,129 @@ $version: "1.0" namespace com.amazonaws.simple use aws.protocols#restJson1 +use smithy.test#httpRequestTests +use smithy.test#httpResponseTests @restJson1 @title("SimpleService") +@documentation("A simple service example, with a Service resource that can be registered and a readonly healthcheck") service SimpleService { + version: "2022-01-01", + resources: [ + Service, + ], operations: [ - AnOperation, + Healthcheck, + StoreServiceBlob, ], } -@http(uri: "/operation", method: "GET") -operation AnOperation { - input: AnOperationInputOutput, - output: AnOperationInputOutput, +@documentation("Id of the service that will be registered") +string ServiceId + +@documentation("Name of the service that will be registered") +string ServiceName + +@error("client") +@documentation( + """ + Returned when a new resource cannot be created because one already exists. + """ +) +structure ResourceAlreadyExists { + @required + message: String +} + +@documentation("A resource that can register services") +resource Service { + identifiers: { id: ServiceId }, + put: RegisterService, +} + +@idempotent +@http(method: "PUT", uri: "/service/{id}") +@documentation("Service register operation") +@httpRequestTests([ + { + id: "RegisterServiceRequestTest", + protocol: "aws.protocols#restJson1", + uri: "/service/1", + headers: { + "Content-Type": "application/json", + }, + params: { id: "1", name: "TestService" }, + body: "{\"name\":\"TestService\"}", + method: "PUT", + } +]) +@httpResponseTests([ + { + id: "RegisterServiceResponseTest", + protocol: "aws.protocols#restJson1", + params: { id: "1", name: "TestService" }, + body: "{\"id\":\"1\",\"name\":\"TestService\"}", + code: 200, + } +]) +operation RegisterService { + input: RegisterServiceInputRequest, + output: RegisterServiceOutputResponse, + errors: [ResourceAlreadyExists] +} + +@documentation("Service register input structure") +structure RegisterServiceInputRequest { + @required + @httpLabel + id: ServiceId, + name: ServiceName, +} + +@documentation("Service register output structure") +structure RegisterServiceOutputResponse { + @required + id: ServiceId, + name: ServiceName, +} + +@readonly +@http(uri: "/healthcheck", method: "GET") +@documentation("Read-only healthcheck operation") +operation Healthcheck { + input: HealthcheckInputRequest, + output: HealthcheckOutputResponse +} + +@documentation("Service healthcheck output structure") +structure HealthcheckInputRequest { + +} + +@documentation("Service healthcheck input structure") +structure HealthcheckOutputResponse { + +} + +@readonly +@http(method: "GET", uri: "/service/{id}/blob") +@documentation("Stores a blob for a service id") +operation StoreServiceBlob { + input: StoreServiceBlobInput, + output: StoreServiceBlobOutput } -structure AnOperationInputOutput { - int: Integer +@documentation("Store a blob for a service id input structure") +structure StoreServiceBlobInput { + @required + @httpLabel + id: ServiceId, + @required + @httpPayload + content: Blob, +} + +@documentation("Store a blob for a service id output structure") +structure StoreServiceBlobOutput { + } From 9d0636872ff66d4258cd8b3b3765d5ea8cb33edd Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 24 May 2022 12:20:44 +0200 Subject: [PATCH 103/255] Fix server unit tests --- .../UnconstrainedCollectionGeneratorTest.kt | 59 ++++++++++--------- .../UnconstrainedMapGeneratorTest.kt | 51 ++++++++-------- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 980e10d855..037b3366da 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace @@ -84,30 +85,32 @@ class UnconstrainedCollectionGeneratorTest { ).render() } } - project.withModule(RustModule.private("unconstrained")) { writer -> - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - writer, - it - ).render() - } + project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> + project.withModule(ModelsModule) { modelsModuleWriter -> + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(listA, listB).forEach { + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + it + ).render() + } - writer.unitTest( - name = "list_a_unconstrained_fail_to_constrain_with_first_error", - test = """ + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_fail_to_constrain_with_first_error", + test = """ let c_builder1 = crate::model::StructureC::builder().int(69); let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected_err = - list_a_unconstrained::ConstraintViolation(list_b_unconstrained::ConstraintViolation( + crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( crate::model::structure_c::ConstraintViolation::MissingString, )); @@ -116,11 +119,11 @@ class UnconstrainedCollectionGeneratorTest { crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); """ - ) + ) - writer.unitTest( - name = "list_a_unconstrained_succeed_to_constrain", - test = """ + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_succeed_to_constrain", + test = """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); @@ -134,20 +137,20 @@ class UnconstrainedCollectionGeneratorTest { assert_eq!(expected, actual); """ - ) + ) - writer.unitTest( - name = "list_a_unconstrained_converts_into_constrained", - test = """ + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_converts_into_constrained", + test = """ let c_builder = crate::model::StructureC::builder(); let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); """ - ) - - project.compileAndTest() + ) + project.compileAndTest() + } } } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index eab8f11ae2..5caa65727e 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace @@ -86,21 +87,24 @@ class UnconstrainedMapGeneratorTest { ).render() } } - project.withModule(RustModule.private("unconstrained")) { writer -> - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - listOf(mapA, mapB).forEach { - UnconstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - writer, - it - ).render() - } + project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> + project.withModule(ModelsModule) { modelsModuleWriter -> + val constraintViolationSymbolProvider = + ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(mapA, mapB).forEach { + UnconstrainedMapGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + it + ).render() + } - // TODO This test is flaky because it depends on the order in which the `HashMap` is visited. + // TODO This test is flaky because it depends on the order in which the `HashMap` is visited. // writer.unitTest( // name = "map_a_unconstrained_fail_to_constrain_with_first_error", // test = """ @@ -130,9 +134,9 @@ class UnconstrainedMapGeneratorTest { // """ // ) - writer.unitTest( - name = "map_a_unconstrained_succeed_to_constrain", - test = """ + unconstrainedModuleWriter.unitTest( + name = "map_a_unconstrained_succeed_to_constrain", + test = """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ @@ -159,11 +163,11 @@ class UnconstrainedMapGeneratorTest { crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); """ - ) + ) - writer.unitTest( - name = "map_a_unconstrained_converts_into_constrained", - test = """ + unconstrainedModuleWriter.unitTest( + name = "map_a_unconstrained_converts_into_constrained", + test = """ let c_builder = crate::model::StructureC::builder(); let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ @@ -178,9 +182,10 @@ class UnconstrainedMapGeneratorTest { let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); """ - ) + ) - project.compileAndTest() + project.compileAndTest() + } } } } From 39b17129e24120039ac867cfacd934f801815474 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 26 May 2022 13:55:06 +0200 Subject: [PATCH 104/255] Fix UnconstrainedMapGeneratorTest.kt --- .../UnconstrainedMapGeneratorTest.kt | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 5caa65727e..3298543261 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -104,35 +104,38 @@ class UnconstrainedMapGeneratorTest { ).render() } - // TODO This test is flaky because it depends on the order in which the `HashMap` is visited. -// writer.unitTest( -// name = "map_a_unconstrained_fail_to_constrain_with_first_error", -// test = """ -// let c_builder1 = crate::model::StructureC::builder().int(69); -// let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); -// let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( -// std::collections::HashMap::from([ -// (String::from("KeyB1"), c_builder1), -// (String::from("KeyB2"), c_builder2), -// ]) -// ); -// let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( -// std::collections::HashMap::from([ -// (String::from("KeyA"), map_b_unconstrained), -// ]) -// ); -// -// let expected_err = -// map_a_unconstrained::ConstraintViolation::Value(map_b_unconstrained::ConstraintViolation::Value( -// crate::model::structure_c::ConstraintViolation::MissingString, -// )); -// -// assert_eq!( -// expected_err, -// std::collections::HashMap::>::try_from(map_a_unconstrained).unwrap_err() -// ); -// """ -// ) + unconstrainedModuleWriter.unitTest( + name = "map_a_unconstrained_fail_to_constrain_with_some_error", + test = """ + let c_builder1 = crate::model::StructureC::builder().int(69); + let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyB1"), c_builder1), + (String::from("KeyB2"), c_builder2), + ]) + ); + let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + std::collections::HashMap::from([ + (String::from("KeyA"), map_b_unconstrained), + ]) + ); + + // Any of these two errors could be returned; it depends on the order in which the maps are visited. + let missing_string_expected_err = + crate::model::map_a::ConstraintViolation::Value(crate::model::map_b::ConstraintViolation::Value( + crate::model::structure_c::ConstraintViolation::MissingString, + )); + let missing_int_expected_err = + crate::model::map_a::ConstraintViolation::Value(crate::model::map_b::ConstraintViolation::Value( + crate::model::structure_c::ConstraintViolation::MissingInt, + )); + + let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); + + assert!(actual_err == missing_string_expected_err || actual_err == missing_int_expected_err); + """ + ) unconstrainedModuleWriter.unitTest( name = "map_a_unconstrained_succeed_to_constrain", From aa1a2fba9ddb17637a8a9b682cae75ec607157cb Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 26 May 2022 14:57:53 +0200 Subject: [PATCH 105/255] Use Display and TryFrom from RuntimeType --- .../server/smithy/generators/ConstrainedMapGenerator.kt | 4 ++-- .../smithy/generators/ConstrainedStringGenerator.kt | 8 ++++---- .../server/smithy/generators/ServerBuilderGenerator.kt | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 7a8cc52a07..3f636b4ed4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -52,7 +52,6 @@ class ConstrainedMapGenerator( "length <= ${lengthTrait.max.get()}" } - // TODO Use TryFrom from Dan's PR. writer.documentShape(shape, model, note = rustDocsNote(name)) writer.rustTemplate( """ @@ -80,7 +79,7 @@ class ConstrainedMapGenerator( type Unconstrained = #{UnconstrainedSymbol}; } - impl std::convert::TryFrom<$inner> for $name { + impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; fn try_from(value: $inner) -> Result { @@ -97,6 +96,7 @@ class ConstrainedMapGenerator( "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "UnconstrainedSymbol" to unconstrainedShapeSymbolProvider.toSymbol(shape), + "TryFrom" to RuntimeType.TryFrom, "ConstraintViolation" to constraintViolation ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 07ce811a55..ec42b253b2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -51,8 +51,6 @@ class ConstrainedStringGenerator( // TODO Docs for `ConstraintViolation`. // TODO Display impl does not honor `sensitive` trait. - // TODO Use Display from Dan's PR. - // TODO Use TryFrom from Dan's PR. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait @@ -89,7 +87,7 @@ class ConstrainedStringGenerator( } } - impl std::fmt::Display for $name { + impl #{Display} for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } @@ -102,7 +100,7 @@ class ConstrainedStringGenerator( } } - impl std::convert::TryFrom<$inner> for $name { + impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; fn try_from(value: $inner) -> Result { @@ -118,6 +116,8 @@ class ConstrainedStringGenerator( "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, "MaybeConstrained" to symbol.wrapMaybeConstrained(), + "Display" to RuntimeType.Display, + "TryFrom" to RuntimeType.TryFrom, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index b7b8e3e54c..f26cfa5763 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -31,7 +31,6 @@ import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -395,11 +394,9 @@ class ServerBuilderGenerator( } private fun renderTryFromBuilderImpl(writer: RustWriter) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1332) `TryFrom` is in Rust 2021's prelude. - // TODO Use Dan's TryFrom. writer.rustTemplate( """ - impl std::convert::TryFrom for #{Structure} { + impl #{TryFrom} for #{Structure} { type Error = ConstraintViolation; fn try_from(builder: Builder) -> Result { @@ -408,6 +405,7 @@ class ServerBuilderGenerator( } """, "Structure" to structureSymbol, + "TryFrom" to RuntimeType.TryFrom, ) } From 6036e9e040c9e550ce563a6efd0f28a3f84fdcab Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 27 May 2022 11:16:36 +0200 Subject: [PATCH 106/255] Add support for enums in payloads --- .../server/smithy/ServerCodegenVisitor.kt | 16 ++++- .../ConstrainedTraitForEnumGenerator.kt | 51 +++++++++++++++ .../generators/ServerBuilderGenerator.kt | 8 ++- .../smithy/generators/ServerEnumGenerator.kt | 63 ++++++++----------- .../smithy/rust/codegen/smithy/Constraints.kt | 7 +-- .../smithy/generators/EnumGenerator.kt | 2 +- .../protocols/parse/JsonParserGenerator.kt | 12 ++-- 7 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 8494ab18e1..fe3e08f49d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedTraitForEnumGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionShapeGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator @@ -337,7 +338,20 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: shape.getTrait()?.also { enum -> logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> - ServerEnumGenerator(model, symbolProvider, writer, shape, enum, codegenContext.runtimeConfig).render() + ServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + enum + ).render() + ConstrainedTraitForEnumGenerator( + model, + symbolProvider, + writer, + shape + ).render() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt new file mode 100644 index 0000000000..6719437af9 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.util.expectTrait + +// TODO Unit tests +/** + * [ConstrainedTraitForEnumGenerator] TODO Docs + */ +class ConstrainedTraitForEnumGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + val writer: RustWriter, + val shape: StringShape +) { + fun render() { + shape.expectTrait() + + val symbol = symbolProvider.toSymbol(shape) + val name = symbol.name + val unconstrainedType = "String" + + writer.rustTemplate( + """ + impl #{ConstrainedTrait} for $name { + type Unconstrained = $unconstrainedType; + } + + impl From<$unconstrainedType> for #{MaybeConstrained} { + fn from(value: $unconstrainedType) -> Self { + Self::Unconstrained(value) + } + } + """, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "MaybeConstrained" to symbol.wrapMaybeConstrained(), + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index f26cfa5763..f86fd0ce0d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -50,7 +50,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. // - This builder is not `PartialEq`. // - Always implements either From for Structure or TryFrom for Structure. -// - `constrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. +// - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. class ServerBuilderGenerator( private val codegenContext: CodegenContext, private val shape: StructureShape, @@ -84,6 +84,7 @@ class ServerBuilderGenerator( } renderImplDisplayConstraintViolation(writer) + // TODO Use RuntimeType.StdError writer.rust("impl std::error::Error for ConstraintViolation { }") // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is @@ -124,6 +125,7 @@ class ServerBuilderGenerator( } // TODO This impl does not take into account sensitive trait. + // TODO Use RuntimeType.Display private fun renderImplDisplayConstraintViolation(writer: RustWriter) { writer.rustBlock("impl std::fmt::Display for ConstraintViolation") { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { @@ -142,6 +144,7 @@ class ServerBuilderGenerator( } private fun renderImplFromConstraintViolationForRequestRejection(writer: RustWriter) { + // TODO Use RuntimeType.From writer.rustTemplate( """ impl From for #{RequestRejection} { @@ -155,6 +158,7 @@ class ServerBuilderGenerator( } private fun renderImplFromBuilderForMaybeConstrained(writer: RustWriter) { + // TODO Use RuntimeType.From writer.rust( """ impl From for #{T} { @@ -410,6 +414,7 @@ class ServerBuilderGenerator( } private fun renderFromBuilderImpl(writer: RustWriter) { + // TODO Use RuntimeType.From writer.rustTemplate( """ impl From for #{Structure} { @@ -434,6 +439,7 @@ class ServerBuilderGenerator( pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } // Strip the `Option` in case the member is not `required`. + // TODO Grep for these and replace them with `extractSymbolFromOption`. .mapRustType { it.stripOuter() } val hadBox = strippedOption.isRustBoxed() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 314af82e8f..2fce74b3d2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -11,84 +11,73 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.CodegenMode -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.util.dq +import software.amazon.smithy.rust.codegen.util.toSnakeCase class ServerEnumGenerator( model: Model, symbolProvider: RustSymbolProvider, + constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val writer: RustWriter, shape: StringShape, enumTrait: EnumTrait, - private val runtimeConfig: RuntimeConfig, ) : EnumGenerator(model, symbolProvider, writer, shape, enumTrait) { override var mode: CodegenMode = CodegenMode.Server - private val errorStruct = "${enumName}UnknownVariantError" + private val unknownVariantSymbol = constraintViolationSymbolProvider.toSymbol(shape) override fun renderFromForStr() { + // TODO Docs for ConstraintViolation + // TODO Display for ConstraintViolation writer.rust( """ - ##[derive(Debug, PartialEq, Eq, Hash)] - pub struct $errorStruct(String); + pub mod ${enumName.toSnakeCase()} { + ##[derive(Debug, PartialEq)] + pub struct ConstraintViolation(pub(crate) String); + } """ ) writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { - write("type Error = $errorStruct;") - writer.rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { - writer.rustBlock("match s") { + rust("type Error = #T;", unknownVariantSymbol) + rustBlock("fn try_from(s: &str) -> Result") { + rustBlock("match s") { sortedMembers.forEach { member -> - write("${member.value.dq()} => Ok($enumName::${member.derivedName()}),") + rust("${member.value.dq()} => Ok($enumName::${member.derivedName()}),") } - write("_ => Err($errorStruct(s.to_owned()))") + rust("_ => Err(#T(s.to_owned()))", unknownVariantSymbol) } } } writer.rustTemplate( """ - impl #{From}<$errorStruct> for #{RequestRejection} { - fn from(e: $errorStruct) -> Self { - Self::EnumVariantNotFound(Box::new(e)) - } - } - - impl #{From}<$errorStruct> for #{JsonDeserialize} { - fn from(e: $errorStruct) -> Self { - Self::custom(format!("unknown variant {}", e)) - } - } - - impl #{StdError} for $errorStruct { } - - impl #{Display} for $errorStruct { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + impl #{TryFrom} for $enumName { + type Error = #{UnknownVariantSymbol}; + fn try_from(s: String) -> Result { + s.try_into() } } """, - "Display" to RuntimeType.Display, - "From" to RuntimeType.From, - "StdError" to RuntimeType.StdError, - "RequestRejection" to ServerRuntimeType.RequestRejection(runtimeConfig), - "JsonDeserialize" to RuntimeType.jsonDeserialize(runtimeConfig), + "TryFrom" to RuntimeType.TryFrom, + "UnknownVariantSymbol" to unknownVariantSymbol ) } override fun renderFromStr() { - writer.rust( + writer.rustTemplate( """ impl std::str::FromStr for $enumName { - type Err = $errorStruct; + type Err = #{UnknownVariantSymbol}; fn from_str(s: &str) -> std::result::Result { - $enumName::try_from(s) + Self::try_from(s) } } - """ + """, + "UnknownVariantSymbol" to unknownVariantSymbol ) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 1b5b030032..42bc4dd40b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -39,12 +39,7 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } } is MapShape -> this.hasTrait() - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) While `enum` traits are constraint traits, we're outright - // rejecting unknown enum variants as deserialization errors instead of parsing them into an unconstrained type - // that we then constrain after parsing the entire request. If the string shape has both the `enum` trait and - // another constraint trait, we warn about this case in [ServerCodegenVisitor]. - // See also https://github.com/awslabs/smithy/issues/1121. - is StringShape -> !this.hasTrait() && this.hasTrait() + is StringShape -> this.hasTrait() || this.hasTrait() else -> false } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt index a71a8d4289..7ac65fdce8 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt @@ -75,7 +75,7 @@ open class EnumGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, - private val shape: StringShape, + protected val shape: StringShape, private val enumTrait: EnumTrait, ) { protected val symbol = symbolProvider.toSymbol(shape) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 559c3e7da9..0a14434d33 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -270,8 +270,8 @@ class JsonParserGenerator( withBlock("$escapedStrName.to_unescaped().map(|u|", ")") { when (target.hasTrait()) { true -> { - if (convertsToEnumInServer(target)) { - rust("#T::try_from(u.as_ref())", symbolProvider.toSymbol(target)) + if (parseUnconstrainedEnum(target)) { + rust("u.into_owned()") } else { rust("#T::from(u.as_ref())", symbolProvider.toSymbol(target)) } @@ -281,12 +281,10 @@ class JsonParserGenerator( } } - private fun convertsToEnumInServer(shape: StringShape) = mode == CodegenMode.Server && shape.hasTrait() + private fun parseUnconstrainedEnum(shape: StringShape) = mode == CodegenMode.Server && shape.hasTrait() private fun RustWriter.deserializeString(target: StringShape) { - // additional .transpose()? because Rust does not allow ? up from closures - val additionalTranspose = if (convertsToEnumInServer(target)) { ".transpose()?".repeat(2) } else { ".transpose()?" } - withBlockTemplate("#{expect_string_or_null}(tokens.next())?.map(|s|", ")$additionalTranspose", *codegenScope) { + withBlockTemplate("#{expect_string_or_null}(tokens.next())?.map(|s|", ").transpose()?", *codegenScope) { deserializeStringInner(target, "s") } } @@ -392,7 +390,7 @@ class JsonParserGenerator( withBlock("let value =", ";") { deserializeMember(shape.value) } - if (convertsToEnumInServer(keyTarget)) { + if (parseUnconstrainedEnum(keyTarget)) { rust("let key = key?;") } if (isSparse) { From 83dd8c8ffd79ef518962a38dba8ec9c46649750a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 30 May 2022 14:22:25 +0200 Subject: [PATCH 107/255] Modify constraints.smithy to take into account enums bound to HTTP message parts --- codegen-server-test/model/constraints.smithy | 67 +++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 87e069e97b..a8a6488763 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -17,9 +17,12 @@ service ConstraintsService { // combination. QueryParamsTargetingLengthMapOperation, QueryParamsTargetingMapOfLengthStringOperation, + //QueryParamsTargetingMapOfEnumStringOperation, QueryParamsTargetingMapOfListOfLengthStringOperation, QueryParamsTargetingMapOfSetOfLengthStringOperation, + //QueryParamsTargetingMapOfListOfEnumStringOperation, HttpPrefixHeadersTargetingLengthMapOperation, + HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -30,7 +33,7 @@ operation ConstrainedShapesOperation { errors: [ErrorWithLengthStringMessage] } -@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "GET") operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, @@ -54,6 +57,12 @@ operation QueryParamsTargetingMapOfLengthStringOperation { output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, } +@http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "GET") +operation QueryParamsTargetingMapOfEnumStringOperation { + input: QueryParamsTargetingMapOfEnumStringOperationInputOutput, + output: QueryParamsTargetingMapOfEnumStringOperationInputOutput, +} + @http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") operation QueryParamsTargetingMapOfListOfLengthStringOperation { input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, @@ -66,12 +75,24 @@ operation QueryParamsTargetingMapOfSetOfLengthStringOperation { output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } +@http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "GET") +operation QueryParamsTargetingMapOfListOfEnumStringOperation { + input: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, + output: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, +} + @http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") operation HttpPrefixHeadersTargetingLengthMapOperation { input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, } +@http(uri: "/http-prefix-headers-targeting-map-of-enum-string-operation", method: "GET") +operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { + input: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, + output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, +} + structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -82,6 +103,10 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpLabel lengthStringLabel: LengthString, + @required + @httpLabel + enumStringLabel: EnumString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working // @required @httpPrefixHeaders("X-Prefix-Headers-") @@ -99,21 +124,35 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpHeader("X-Length-List") lengthStringListHeader: ListOfLengthString, + @httpHeader("X-Enum-List") + enumStringListHeader: ListOfEnumString, + @httpQuery("lengthString") lengthStringQuery: LengthString, + @httpQuery("enumString") + enumStringQuery: EnumString, + @httpQuery("lengthStringList") lengthStringListQuery: ListOfLengthString, @httpQuery("lengthStringSet") lengthStringSetQuery: SetOfLengthString, + + @httpQuery("enumStringList") + enumStringListQuery: ListOfEnumString, } structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { - @httpPrefixHeaders("X-Prefix-Headers-") + @httpPrefixHeaders("X-Prefix-Headers-LengthMap-") lengthMap: ConBMap, } +structure HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput { + @httpPrefixHeaders("X-Prefix-Headers-MapOfEnumString-") + mapOfEnumString: MapOfEnumString, +} + structure QueryParamsTargetingLengthMapOperationInputOutput { @httpQueryParams lengthMap: ConBMap @@ -124,6 +163,11 @@ structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { mapOfLengthString: MapOfLengthString } +structure QueryParamsTargetingMapOfEnumStringOperationInputOutput { + @httpQueryParams + mapOfEnumString: MapOfEnumString +} + structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { @httpQueryParams mapOfListOfLengthString: MapOfListOfLengthString @@ -134,6 +178,11 @@ structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { mapOfSetOfLengthString: MapOfSetOfLengthString } +structure QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput { + @httpQueryParams + mapOfListOfEnumString: MapOfListOfEnumString +} + structure ConA { @required conB: ConB, @@ -167,11 +216,21 @@ map MapOfLengthString { value: LengthString, } +map MapOfEnumString { + key: EnumString, + value: EnumString, +} + map MapOfListOfLengthString { key: LengthString, value: ListOfLengthString, } +map MapOfListOfEnumString { + key: EnumString, + value: ListOfEnumString, +} + map MapOfSetOfLengthString { key: LengthString, value: SetOfLengthString, @@ -231,6 +290,10 @@ list ListOfLengthString { member: LengthString } +list ListOfEnumString { + member: EnumString +} + structure ConB { @required nice: String, From effa0e52a1b07f299adcafb8de93ca7a23b18381 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 30 May 2022 14:22:49 +0200 Subject: [PATCH 108/255] Revert "Modify constraints.smithy to take into account enums bound to HTTP message parts" This reverts commit 83dd8c8ffd79ef518962a38dba8ec9c46649750a. --- codegen-server-test/model/constraints.smithy | 67 +------------------- 1 file changed, 2 insertions(+), 65 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index a8a6488763..87e069e97b 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -17,12 +17,9 @@ service ConstraintsService { // combination. QueryParamsTargetingLengthMapOperation, QueryParamsTargetingMapOfLengthStringOperation, - //QueryParamsTargetingMapOfEnumStringOperation, QueryParamsTargetingMapOfListOfLengthStringOperation, QueryParamsTargetingMapOfSetOfLengthStringOperation, - //QueryParamsTargetingMapOfListOfEnumStringOperation, HttpPrefixHeadersTargetingLengthMapOperation, - HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -33,7 +30,7 @@ operation ConstrainedShapesOperation { errors: [ErrorWithLengthStringMessage] } -@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "GET") +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, @@ -57,12 +54,6 @@ operation QueryParamsTargetingMapOfLengthStringOperation { output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "GET") -operation QueryParamsTargetingMapOfEnumStringOperation { - input: QueryParamsTargetingMapOfEnumStringOperationInputOutput, - output: QueryParamsTargetingMapOfEnumStringOperationInputOutput, -} - @http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") operation QueryParamsTargetingMapOfListOfLengthStringOperation { input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, @@ -75,24 +66,12 @@ operation QueryParamsTargetingMapOfSetOfLengthStringOperation { output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "GET") -operation QueryParamsTargetingMapOfListOfEnumStringOperation { - input: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, - output: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, -} - @http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") operation HttpPrefixHeadersTargetingLengthMapOperation { input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, } -@http(uri: "/http-prefix-headers-targeting-map-of-enum-string-operation", method: "GET") -operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { - input: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, - output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, -} - structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -103,10 +82,6 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpLabel lengthStringLabel: LengthString, - @required - @httpLabel - enumStringLabel: EnumString, - // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working // @required @httpPrefixHeaders("X-Prefix-Headers-") @@ -124,35 +99,21 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpHeader("X-Length-List") lengthStringListHeader: ListOfLengthString, - @httpHeader("X-Enum-List") - enumStringListHeader: ListOfEnumString, - @httpQuery("lengthString") lengthStringQuery: LengthString, - @httpQuery("enumString") - enumStringQuery: EnumString, - @httpQuery("lengthStringList") lengthStringListQuery: ListOfLengthString, @httpQuery("lengthStringSet") lengthStringSetQuery: SetOfLengthString, - - @httpQuery("enumStringList") - enumStringListQuery: ListOfEnumString, } structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { - @httpPrefixHeaders("X-Prefix-Headers-LengthMap-") + @httpPrefixHeaders("X-Prefix-Headers-") lengthMap: ConBMap, } -structure HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput { - @httpPrefixHeaders("X-Prefix-Headers-MapOfEnumString-") - mapOfEnumString: MapOfEnumString, -} - structure QueryParamsTargetingLengthMapOperationInputOutput { @httpQueryParams lengthMap: ConBMap @@ -163,11 +124,6 @@ structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { mapOfLengthString: MapOfLengthString } -structure QueryParamsTargetingMapOfEnumStringOperationInputOutput { - @httpQueryParams - mapOfEnumString: MapOfEnumString -} - structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { @httpQueryParams mapOfListOfLengthString: MapOfListOfLengthString @@ -178,11 +134,6 @@ structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { mapOfSetOfLengthString: MapOfSetOfLengthString } -structure QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput { - @httpQueryParams - mapOfListOfEnumString: MapOfListOfEnumString -} - structure ConA { @required conB: ConB, @@ -216,21 +167,11 @@ map MapOfLengthString { value: LengthString, } -map MapOfEnumString { - key: EnumString, - value: EnumString, -} - map MapOfListOfLengthString { key: LengthString, value: ListOfLengthString, } -map MapOfListOfEnumString { - key: EnumString, - value: ListOfEnumString, -} - map MapOfSetOfLengthString { key: LengthString, value: SetOfLengthString, @@ -290,10 +231,6 @@ list ListOfLengthString { member: LengthString } -list ListOfEnumString { - member: EnumString -} - structure ConB { @required nice: String, From 3afae8e4d84539973de18272f894f443155e62fc Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 31 May 2022 18:53:58 +0200 Subject: [PATCH 109/255] Constrained unions work --- codegen-server-test/build.gradle.kts | 4 +- codegen-server-test/model/constraints.smithy | 16 +- .../ConstraintViolationSymbolProvider.kt | 12 +- .../server/smithy/ServerCodegenVisitor.kt | 25 ++- .../generators/ServerBuilderGenerator.kt | 13 +- .../smithy/generators/ServerEnumGenerator.kt | 1 - .../generators/UnconstrainedUnionGenerator.kt | 190 ++++++++++++++++++ .../smithy/rust/codegen/smithy/Constraints.kt | 6 - .../UnconstrainedShapeSymbolProvider.kt | 52 +++-- .../protocols/parse/JsonParserGenerator.kt | 39 ++-- 10 files changed, 288 insertions(+), 70 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 6750bb0b6f..53f521e6b4 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -35,9 +35,9 @@ dependencies { val allCodegenTests = listOf( // CodegenTest("com.amazonaws.simple#SimpleService", "simple"), -// CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), + CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints") // CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), - CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation") +// CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), // CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), // CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), // CodegenTest("aws.protocoltests.misc#MiscService", "misc"), diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 87e069e97b..f20fc8ec94 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -154,7 +154,7 @@ structure ConA { mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - //unionWithConstrainedStructureVariant: UnionWithConstrainedStructureVariant, + constrainedUnion: ConstrainedUnion, enumString: EnumString, listOfLengthString: ListOfLengthString, @@ -198,13 +198,15 @@ string FixedLengthString @length(min: 1, max: 69) string MediaTypeLengthString -union UnionWithConstrainedStructureVariant { - constrainedStructureVariant: ConstrainedStructureVariant -} +/// A union with constrained members. +union ConstrainedUnion { + enumString: EnumString, + lengthString: LengthString, -structure ConstrainedStructureVariant { - @required - int: Integer + constrainedStructure: ConB, + conBList: ConBList, + conBSet: ConBSet, + conBMap: ConBMap, } @enum([ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 5c5356729a..7cc300b142 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -67,8 +67,8 @@ class ConstraintViolationSymbolProvider( ) : WrappingSymbolProvider(base) { private val constraintViolationName = "ConstraintViolation" - private fun constraintViolationSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is CollectionShape || shape is MapShape) + private fun constraintViolationSymbolForCollectionOrMapOrUnionShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape is MapShape || shape is UnionShape) val symbol = base.toSymbol(shape) val constraintViolationNamespace = @@ -90,8 +90,8 @@ class ConstraintViolationSymbolProvider( check(shape.canReachConstrainedShape(model, base)) return when (shape) { - is MapShape, is CollectionShape -> { - constraintViolationSymbolForCollectionOrMapShape(shape) + is MapShape, is CollectionShape, is UnionShape -> { + constraintViolationSymbolForCollectionOrMapOrUnionShape(shape) } is StructureShape -> { val builderSymbol = shape.builderSymbol(base) @@ -105,10 +105,6 @@ class ConstraintViolationSymbolProvider( .definitionFile(Unconstrained.filename) .build() } - is UnionShape -> { - // TODO - base.toSymbol(shape) - } is StringShape -> { val namespace = "crate::${Models.namespace}::${ RustReservedWords.escapeIfNeeded( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 507ba671ea..ac1e9df5d3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -32,6 +32,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServic import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.Constrained @@ -385,10 +386,32 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: * This function _does not_ generate any serializers. */ override fun unionShape(shape: UnionShape) { - logger.info("[rust-server-codegen] Generating an union $shape") + logger.info("[rust-server-codegen] Generating an union shape $shape") rustCrate.useShapeWriter(shape) { UnionGenerator(model, symbolProvider, it, shape, renderUnknownVariant = false).render() } + + if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + model, + symbolProvider + ) + ) { + logger.info("[rust-server-codegen] Generating an unconstrained type for union shape $shape") + rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> + UnconstrainedUnionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + shape + ).render() + } + } + } } /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index f86fd0ce0d..ecd9166567 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -79,6 +80,7 @@ class ServerBuilderGenerator( // TODO(): `#[non_exhaustive] unless we commit to making builders of builders public. writer.docs("Holds one variant for each of the ways the builder can fail.") Attribute.NonExhaustive.render(writer) + // TODO This should use `name` from the constraint violation symbol. writer.rustBlock("pub enum ConstraintViolation") { constraintViolations().forEach { renderConstraintViolation(this, it) } } @@ -263,7 +265,8 @@ class ServerBuilderGenerator( /** * Returns whether the constrained builder member type (the type on which the `Constrained` trait is implemented) * is the final type the user sees when receiving the built struct. This is true when the corresponding constrained - * type is public and not `pub(crate)`, which happens when the target is a structure or is directly constrained. + * type is public and not `pub(crate)`, which happens when the target is a structure shape, a union shape, or is + * directly constrained. * * An example where this returns false is when the member shape targets a list whose members are lists of structures * having at least one `required` member. In this case the member shape is transitively but not directly constrained, @@ -272,8 +275,12 @@ class ServerBuilderGenerator( * * See [PubCrateConstrainedShapeSymbolProvider] too. */ - fun constrainedTypeHoldsFinalType(member: MemberShape) = - model.expectShape(member.target).isStructureShape || member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider) + fun constrainedTypeHoldsFinalType(member: MemberShape): Boolean { + val targetShape = model.expectShape(member.target) + return targetShape is StructureShape || + targetShape is UnionShape || + member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider) + } /** * Render a `set_foo` method. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 821254a893..29549e50e7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -29,7 +29,6 @@ class ServerEnumGenerator( ) : EnumGenerator(model, symbolProvider, writer, shape, enumTrait) { override var target: CodegenTarget = CodegenTarget.SERVER private val unknownVariantSymbol = constraintViolationSymbolProvider.toSymbol(shape) - private val errorStruct = "${enumName}UnknownVariantError" override fun renderFromForStr() { // TODO Docs for ConstraintViolation diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt new file mode 100644 index 0000000000..52f01c31d4 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -0,0 +1,190 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.withBlock +import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.smithy.letIf +import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.util.hasTrait +import software.amazon.smithy.rust.codegen.util.toPascalCase + +// TODO Docs +// TODO UnitTests +class UnconstrainedUnionGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val unconstrainedModuleWriter: RustWriter, + private val modelsModuleWriter: RustWriter, + val shape: UnionShape +) { + private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } + + fun render() { + check(shape.canReachConstrainedShape(model, symbolProvider)) + + val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name + val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) + val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) + val constraintViolationName = constraintViolationSymbol.name + + unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { + rustBlock( + """ + ##[derive(Debug, Clone)] + pub(crate) enum $name + """ + ) { + sortedMembers.forEach { member -> + rust( + "${unconstrainedShapeSymbolProvider.toMemberName(member)}(#T),", + unconstrainedShapeSymbolProvider.toSymbol(member) + ) + } + } + + rustTemplate( + """ + impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { + type Error = #{ConstraintViolationSymbol}; + + fn try_from(value: $name) -> Result { + #{body:W} + } + } + """, + "ConstrainedSymbol" to constrainedSymbol, + "ConstraintViolationSymbol" to constraintViolationSymbol, + "body" to generateTryFromUnconstrainedUnionImpl(), + ) + } + + modelsModuleWriter.rustTemplate( + """ + impl #{ConstrainedTrait} for #{ConstrainedSymbol} { + type Unconstrained = #{UnconstrainedSymbol}; + } + + impl From<#{UnconstrainedSymbol}> for #{MaybeConstrained} { + fn from(value: #{UnconstrainedSymbol}) -> Self { + Self::Unconstrained(value) + } + } + """, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "ConstrainedSymbol" to constrainedSymbol, + "UnconstrainedSymbol" to symbol + ) + + modelsModuleWriter.withModule( + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + ) { + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(this) + rustBlock("pub enum $constraintViolationName") { + constraintViolations().forEach { renderConstraintViolation(this, it) } + } + } + } + + data class ConstraintViolation(val forMember: MemberShape) { + fun name() = "${forMember.memberName.toPascalCase()}ConstraintViolation" + } + + private fun constraintViolations() = + sortedMembers + .filter { it.targetCanReachConstrainedShape(model, symbolProvider) } + .map { ConstraintViolation(it) } + + // TODO I expect we can deduplicate a bit of code; this was copied from [ServerBuilderGenerator.renderConstraintViolation]. + private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) { + val targetShape = model.expectShape(constraintViolation.forMember.target) + + val constraintViolationSymbol = + constraintViolationSymbolProvider.toSymbol(targetShape) + // If the corresponding union's member is boxed, box this constraint violation symbol too. + .letIf(constraintViolation.forMember.hasTrait()) { + it.makeRustBoxed() + } + + writer.rust( + "${constraintViolation.name()}(#T),", + constraintViolationSymbol + ) + } + + private fun generateTryFromUnconstrainedUnionImpl() = writable { + withBlock("Ok(", ")") { + withBlock("match value {", "}") { + sortedMembers.forEach { member -> + val memberName = unconstrainedShapeSymbolProvider.toMemberName(member) + withBlockTemplate( + "#{UnconstrainedUnion}::$memberName(unconstrained) => Self::$memberName(", + "),", + "UnconstrainedUnion" to symbol, + ) { + if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + val targetShape = model.expectShape(member.target) + val resolveToNonPublicConstrainedType = + !targetShape.isDirectlyConstrained(symbolProvider) && + !targetShape.isStructureShape + + if (resolveToNonPublicConstrainedType) { + rustTemplate( + """ + { + let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained + .try_into() + .map_err(Self::Error::${ConstraintViolation(member).name()})?; + constrained.into() + } + """, + "PubCrateConstrainedShapeSymbol" to pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) + ) + } else { + rust( + """ + unconstrained + .try_into() + .map_err(Self::Error::${ConstraintViolation(member).name()})? + """ + ) + } + } else { + rust("unconstrained") + } + } + } + } + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 61cfb2caa6..42bc4dd40b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -8,7 +8,6 @@ import software.amazon.smithy.model.shapes.MemberShape 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.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait @@ -74,11 +73,6 @@ fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, // so we can't simply delegate to the `else` branch when we implement them. this.targetCanReachConstrainedShape(model, symbolProvider) - } else if (this is UnionShape) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Union shapes with constrained variants are not yet - // implemented, so we have to be careful and calculate whether there's at least one directly constrained shape - // that is _not_ reachable via a union. - false } else { Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } } \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 2ed9bd977c..4f42c624fa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -15,6 +15,7 @@ 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.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol @@ -28,8 +29,9 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase * * For collection and map shapes, this type is a [RustType.Opaque] wrapper * tuple newtype holding a container over the inner unconstrained type. For - * structure shapes, it's their builder type. For simple shapes, it's whatever - * the regular base symbol provider returns. + * structure shapes, it's their builder type. For union shapes, it's an enum + * whose variants are the corresponding unconstrained variants. For simple + * shapes, it's whatever the regular base symbol provider returns. * * So, for example, given the following model: * @@ -72,10 +74,10 @@ class UnconstrainedShapeSymbolProvider( ) : WrappingSymbolProvider(base) { private val nullableIndex = NullableIndex.of(model) - private fun unconstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is CollectionShape || shape is MapShape) + private fun unconstrainedSymbolForCollectionOrMapOrUnionShape(shape: Shape): Symbol { + check(shape is CollectionShape || shape is MapShape || shape is UnionShape) - val name = unconstrainedTypeNameForCollectionOrMapShape(shape, serviceShape) + val name = unconstrainedTypeNameForCollectionOrMapOrUnionShape(shape, serviceShape) val namespace = "crate::${Unconstrained.namespace}::${RustReservedWords.escapeIfNeeded(name.toSnakeCase())}" val rustType = RustType.Opaque(name, namespace) return Symbol.builder() @@ -90,14 +92,14 @@ class UnconstrainedShapeSymbolProvider( when (shape) { is CollectionShape -> { if (shape.canReachConstrainedShape(model, base)) { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForCollectionOrMapOrUnionShape(shape) } else { base.toSymbol(shape) } } is MapShape -> { if (shape.canReachConstrainedShape(model, base)) { - unconstrainedSymbolForCollectionOrMapShape(shape) + unconstrainedSymbolForCollectionOrMapOrUnionShape(shape) } else { base.toSymbol(shape) } @@ -109,18 +111,24 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } } + is UnionShape -> { + if (shape.canReachConstrainedShape(model, base)) { + unconstrainedSymbolForCollectionOrMapOrUnionShape(shape) + } else { + base.toSymbol(shape) + } + } is MemberShape -> { - // The only case where we use this symbol provider on a member shape is when generating deserializers - // for HTTP-bound member shapes. See, for example: - // * how [HttpBindingGenerator] generates deserializers for a member shape with the `httpPrefixHeaders` - // trait targeting a map shape of string keys and values; or - // * how [ServerHttpBoundProtocolGenerator] deserializes for a member shape with the `httpQuery` trait - // targeting a collection shape that can reach a constrained shape. - if (model.expectShape(shape.container).isStructureShape && shape.targetCanReachConstrainedShape( - model, - base - ) - ) { + // There are only two cases where we use this symbol provider on a member shape. + // + // 1. When generating deserializers for HTTP-bound member shapes. See, for example: + // * how [HttpBindingGenerator] generates deserializers for a member shape with the `httpPrefixHeaders` + // trait targeting a map shape of string keys and values; or + // * how [ServerHttpBoundProtocolGenerator] deserializes for a member shape with the `httpQuery` + // trait targeting a collection shape that can reach a constrained shape. + // + // 2. When generating members for unconstrained unions. See [UnconstrainedUnionGenerator]. + if (shape.targetCanReachConstrainedShape(model, base)) { val targetShape = model.expectShape(shape.target) val targetSymbol = this.toSymbol(targetShape) // Handle boxing first so we end up with `Option>`, not `Box>`. @@ -142,7 +150,11 @@ class UnconstrainedShapeSymbolProvider( } } -fun unconstrainedTypeNameForCollectionOrMapShape(shape: Shape, serviceShape: ServiceShape): String { - check(shape is CollectionShape || shape is MapShape) +/** + * Unconstrained type names are always suffixed with `Unconstrained` for clarity, even though we could dispense with it + * given that they all live inside the `unconstrained` module, so they don't collide with the constrained types. + */ +fun unconstrainedTypeNameForCollectionOrMapOrUnionShape(shape: Shape, serviceShape: ServiceShape): String { + check(shape is CollectionShape || shape is MapShape || shape is UnionShape) return "${shape.id.getName(serviceShape).toPascalCase()}Unconstrained" } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 94ba8d6e85..6635ef4ac0 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.model.shapes.OperationShape +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.model.shapes.TimestampShape @@ -242,10 +243,9 @@ class JsonParserGenerator( } } - private fun RustWriter.deserializeMember(memberShape: MemberShape, unionMemberREMOVE_ME: Boolean = false) { - val target = model.expectShape(memberShape.target) - when (target) { - is StringShape -> deserializeString(target, unionMemberREMOVE_ME) + private fun RustWriter.deserializeMember(memberShape: MemberShape) { + when (val target = model.expectShape(memberShape.target)) { + is StringShape -> deserializeString(target) is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope) is NumberShape -> deserializeNumber(target) is BlobShape -> rustTemplate("#{expect_blob_or_null}(tokens.next())?", *codegenScope) @@ -267,23 +267,11 @@ class JsonParserGenerator( } } - private fun RustWriter.deserializeStringInner(target: StringShape, escapedStrName: String, unionMemberREMOVE_ME: Boolean = false) { + private fun RustWriter.deserializeStringInner(target: StringShape, escapedStrName: String) { withBlock("$escapedStrName.to_unescaped().map(|u|", ")") { when (target.hasTrait()) { true -> { - if (codegenTarget == CodegenTarget.SERVER && unionMemberREMOVE_ME) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Union shapes with constrained variants - // are not yet implemented. - rust( - """ - { - use std::str::FromStr; - #T::from_str(u.as_ref()).expect("Remove me") - } - """, - symbolProvider.toSymbol(target) - ) - } else if (parseUnconstrainedEnum(target)) { + if (parseUnconstrainedEnum(target)) { rust("u.into_owned()") } else { rust("#T::from(u.as_ref())", symbolProvider.toSymbol(target)) @@ -296,9 +284,9 @@ class JsonParserGenerator( private fun parseUnconstrainedEnum(shape: StringShape) = codegenTarget == CodegenTarget.SERVER && shape.hasTrait() - private fun RustWriter.deserializeString(target: StringShape, unionMemberREMOVE_ME: Boolean = false) { + private fun RustWriter.deserializeString(target: StringShape) { withBlockTemplate("#{expect_string_or_null}(tokens.next())?.map(|s|", ").transpose()?", *codegenScope) { - deserializeStringInner(target, "s", unionMemberREMOVE_ME) + deserializeStringInner(target, "s") } } @@ -468,9 +456,16 @@ class JsonParserGenerator( rust("#T(tokens)?", nestedParser) } + // TODO Grep for `returnUnconstrainedType` and `parseUnconstrainedEnum`. They should _all_ use this method. + private fun parseUnconstrainedShape(shape: Shape) = codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) + private fun RustWriter.deserializeUnion(shape: UnionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) - val symbol = symbolProvider.toSymbol(shape) + val symbol = if (parseUnconstrainedShape(shape)) { + unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + symbolProvider.toSymbol(shape) + } val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ @@ -503,7 +498,7 @@ class JsonParserGenerator( val variantName = symbolProvider.toMemberName(member) rustBlock("${jsonName(member).dq()} =>") { withBlock("Some(#T::$variantName(", "))", symbol) { - deserializeMember(member, unionMemberREMOVE_ME = true) + deserializeMember(member) unwrapOrDefaultOrError(member) } } From a52886d72f65f9f25d733b66ed95ee0b27844124 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 31 May 2022 20:36:26 +0200 Subject: [PATCH 110/255] Fix server enum unit tests --- .../generators/ServerEnumGeneratorTest.kt | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 7842b35566..881fe05b97 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -9,8 +9,8 @@ import io.kotest.matchers.string.shouldNotContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest import software.amazon.smithy.rust.codegen.util.expectTrait @@ -36,30 +36,44 @@ class ServerEnumGeneratorTest { string InstanceType """.asSmithyModel() + private val codegenContext = serverTestCodegenContext(model) + private val symbolProvider = codegenContext.symbolProvider + private val serviceShape = codegenContext.serviceShape + private val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + private val writer = RustWriter.forModule("model") + private val shape = model.lookup("test#InstanceType") + @Test fun `it generates TryFrom, FromStr and errors for enums`() { - val provider = serverTestSymbolProvider(model) - val writer = RustWriter.forModule("model") - val shape = model.lookup("test#InstanceType") - val generator = ServerEnumGenerator(model, provider, writer, shape, shape.expectTrait(), TestRuntimeConfig) - generator.render() + ServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + shape.expectTrait() + ).render() + // TODO Last line needs to be replaced with the `ConstraintViolation`. writer.compileAndTest( """ use std::str::FromStr; assert_eq!(InstanceType::try_from("t2.nano").unwrap(), InstanceType::T2Nano); assert_eq!(InstanceType::from_str("t2.nano").unwrap(), InstanceType::T2Nano); - assert_eq!(InstanceType::try_from("unknown").unwrap_err(), InstanceTypeUnknownVariantError("unknown".to_string())); + //assert_eq!(InstanceType::try_from("unknown").unwrap_err(), InstanceTypeUnknownVariantError("unknown".to_string())); """ ) } @Test fun `it generates enums without the unknown variant`() { - val provider = serverTestSymbolProvider(model) - val writer = RustWriter.forModule("model") - val shape = model.lookup("test#InstanceType") - val generator = ServerEnumGenerator(model, provider, writer, shape, shape.expectTrait(), TestRuntimeConfig) - generator.render() + ServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + shape.expectTrait() + ).render() writer.compileAndTest( """ // check no unknown @@ -74,11 +88,14 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without non_exhaustive`() { - val provider = serverTestSymbolProvider(model) - val writer = RustWriter.forModule("model") - val shape = model.lookup("test#InstanceType") - val generator = ServerEnumGenerator(model, provider, writer, shape, shape.expectTrait(), TestRuntimeConfig) - generator.render() + ServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + shape.expectTrait() + ).render() writer.toString() shouldNotContain "#[non_exhaustive]" } } From 71fb17f53c3f528a7969f9e4e74678e0d93c7aad Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 31 May 2022 20:38:24 +0200 Subject: [PATCH 111/255] Revert "Revert "Modify constraints.smithy to take into account enums bound to HTTP message parts"" This reverts commit effa0e52a1b07f299adcafb8de93ca7a23b18381. --- codegen-server-test/model/constraints.smithy | 67 +++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index f20fc8ec94..971f2ff90c 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -17,9 +17,12 @@ service ConstraintsService { // combination. QueryParamsTargetingLengthMapOperation, QueryParamsTargetingMapOfLengthStringOperation, + //QueryParamsTargetingMapOfEnumStringOperation, QueryParamsTargetingMapOfListOfLengthStringOperation, QueryParamsTargetingMapOfSetOfLengthStringOperation, + //QueryParamsTargetingMapOfListOfEnumStringOperation, HttpPrefixHeadersTargetingLengthMapOperation, + HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -30,7 +33,7 @@ operation ConstrainedShapesOperation { errors: [ErrorWithLengthStringMessage] } -@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}", method: "GET") +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "GET") operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, @@ -54,6 +57,12 @@ operation QueryParamsTargetingMapOfLengthStringOperation { output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, } +@http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "GET") +operation QueryParamsTargetingMapOfEnumStringOperation { + input: QueryParamsTargetingMapOfEnumStringOperationInputOutput, + output: QueryParamsTargetingMapOfEnumStringOperationInputOutput, +} + @http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") operation QueryParamsTargetingMapOfListOfLengthStringOperation { input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, @@ -66,12 +75,24 @@ operation QueryParamsTargetingMapOfSetOfLengthStringOperation { output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } +@http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "GET") +operation QueryParamsTargetingMapOfListOfEnumStringOperation { + input: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, + output: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, +} + @http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") operation HttpPrefixHeadersTargetingLengthMapOperation { input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, } +@http(uri: "/http-prefix-headers-targeting-map-of-enum-string-operation", method: "GET") +operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { + input: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, + output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, +} + structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -82,6 +103,10 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpLabel lengthStringLabel: LengthString, + @required + @httpLabel + enumStringLabel: EnumString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working // @required @httpPrefixHeaders("X-Prefix-Headers-") @@ -99,21 +124,35 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpHeader("X-Length-List") lengthStringListHeader: ListOfLengthString, + @httpHeader("X-Enum-List") + enumStringListHeader: ListOfEnumString, + @httpQuery("lengthString") lengthStringQuery: LengthString, + @httpQuery("enumString") + enumStringQuery: EnumString, + @httpQuery("lengthStringList") lengthStringListQuery: ListOfLengthString, @httpQuery("lengthStringSet") lengthStringSetQuery: SetOfLengthString, + + @httpQuery("enumStringList") + enumStringListQuery: ListOfEnumString, } structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { - @httpPrefixHeaders("X-Prefix-Headers-") + @httpPrefixHeaders("X-Prefix-Headers-LengthMap-") lengthMap: ConBMap, } +structure HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput { + @httpPrefixHeaders("X-Prefix-Headers-MapOfEnumString-") + mapOfEnumString: MapOfEnumString, +} + structure QueryParamsTargetingLengthMapOperationInputOutput { @httpQueryParams lengthMap: ConBMap @@ -124,6 +163,11 @@ structure QueryParamsTargetingMapOfLengthStringOperationInputOutput { mapOfLengthString: MapOfLengthString } +structure QueryParamsTargetingMapOfEnumStringOperationInputOutput { + @httpQueryParams + mapOfEnumString: MapOfEnumString +} + structure QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput { @httpQueryParams mapOfListOfLengthString: MapOfListOfLengthString @@ -134,6 +178,11 @@ structure QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput { mapOfSetOfLengthString: MapOfSetOfLengthString } +structure QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput { + @httpQueryParams + mapOfListOfEnumString: MapOfListOfEnumString +} + structure ConA { @required conB: ConB, @@ -167,11 +216,21 @@ map MapOfLengthString { value: LengthString, } +map MapOfEnumString { + key: EnumString, + value: EnumString, +} + map MapOfListOfLengthString { key: LengthString, value: ListOfLengthString, } +map MapOfListOfEnumString { + key: EnumString, + value: ListOfEnumString, +} + map MapOfSetOfLengthString { key: LengthString, value: SetOfLengthString, @@ -233,6 +292,10 @@ list ListOfLengthString { member: LengthString } +list ListOfEnumString { + member: EnumString +} + structure ConB { @required nice: String, From 8d3f434f1faf656228c4e33f363cbdc687fc62d5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 1 Jun 2022 10:53:00 +0200 Subject: [PATCH 112/255] Uncomment codegen server integration tests --- codegen-server-test/build.gradle.kts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 53f521e6b4..d124d843aa 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -34,16 +34,16 @@ dependencies { } val allCodegenTests = listOf( -// CodegenTest("com.amazonaws.simple#SimpleService", "simple"), - CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints") -// CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), -// CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), -// CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), -// CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), -// CodegenTest("aws.protocoltests.misc#MiscService", "misc"), -// CodegenTest("com.amazonaws.ebs#Ebs", "ebs"), -// CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), -// CodegenTest("com.aws.example#PokemonService", "pokemon_service_sdk") + CodegenTest("com.amazonaws.simple#SimpleService", "simple"), + CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), + CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), + CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), + CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), + CodegenTest("aws.protocoltests.misc#MiscService", "misc"), + CodegenTest("com.amazonaws.ebs#Ebs", "ebs"), + CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), + CodegenTest("com.aws.example#PokemonService", "pokemon_service_sdk") ) tasks.register("generateSmithyBuild") { From 6496b474a39e28a4b73f185f491c4ea9cb156972 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 1 Jun 2022 16:36:34 +0200 Subject: [PATCH 113/255] Modify constraints.smithy to make it pass, link issues --- codegen-server-test/model/constraints.smithy | 15 ++++++++++----- .../generators/http/HttpBindingGenerator.kt | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 971f2ff90c..559ed7a92a 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -17,12 +17,13 @@ service ConstraintsService { // combination. QueryParamsTargetingLengthMapOperation, QueryParamsTargetingMapOfLengthStringOperation, - //QueryParamsTargetingMapOfEnumStringOperation, + QueryParamsTargetingMapOfEnumStringOperation, QueryParamsTargetingMapOfListOfLengthStringOperation, QueryParamsTargetingMapOfSetOfLengthStringOperation, - //QueryParamsTargetingMapOfListOfEnumStringOperation, + QueryParamsTargetingMapOfListOfEnumStringOperation, HttpPrefixHeadersTargetingLengthMapOperation, - HttpPrefixHeadersTargetingMapOfEnumStringOperation, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + //HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -124,8 +125,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpHeader("X-Length-List") lengthStringListHeader: ListOfLengthString, - @httpHeader("X-Enum-List") - enumStringListHeader: ListOfEnumString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + //@httpHeader("X-Enum") + //enumStringHeader: EnumString, + + //@httpHeader("X-Enum-List") + //enumStringListHeader: ListOfEnumString, @httpQuery("lengthString") lengthStringQuery: LengthString, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index b6a4a2c541..27019eb5f6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -552,6 +552,7 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> val listHeader = memberType is CollectionShape + // TODO This should be a method. val workingWithPublicConstrainedWrapperTupleType = codegenTarget == CodegenTarget.SERVER && memberShape.hasPublicConstrainedWrapperTupleType(model) rustTemplate( From 2744eb5154b99a636c26044e37a59a1ddbeb4669 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 1 Jun 2022 16:47:37 +0200 Subject: [PATCH 114/255] Address some easy TODOs --- codegen-server-test/model/constraints.smithy | 8 +- .../server/smithy/RustCodegenServerPlugin.kt | 18 +++-- .../server/smithy/ServerCodegenVisitor.kt | 4 +- .../generators/ConstrainedStringGenerator.kt | 5 +- .../ConstrainedTraitForEnumGenerator.kt | 4 +- .../generators/ServerBuilderGenerator.kt | 60 +++++++------- .../smithy/generators/ServerEnumGenerator.kt | 2 - .../UnconstrainedCollectionGenerator.kt | 9 ++- .../generators/UnconstrainedMapGenerator.kt | 34 ++------ .../generators/UnconstrainedUnionGenerator.kt | 8 +- .../ServerHttpBoundProtocolGenerator.kt | 35 ++++---- .../smithy/testutil/ServerTestHelpers.kt | 5 +- .../generators/ServerEnumGeneratorTest.kt | 3 +- .../rust/codegen/smithy/SymbolVisitor.kt | 33 ++++---- .../UnconstrainedShapeSymbolProvider.kt | 2 +- .../smithy/generators/error/ErrorGenerator.kt | 4 +- .../generators/http/HttpBindingGenerator.kt | 12 ++- .../protocols/parse/JsonParserGenerator.kt | 80 ++++++++++--------- .../smithy/protocols/EventStreamTestTools.kt | 2 +- 19 files changed, 157 insertions(+), 171 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 559ed7a92a..60fcf48003 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -23,7 +23,7 @@ service ConstraintsService { QueryParamsTargetingMapOfListOfEnumStringOperation, HttpPrefixHeadersTargetingLengthMapOperation, // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - //HttpPrefixHeadersTargetingMapOfEnumStringOperation, + // HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -126,11 +126,11 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { lengthStringListHeader: ListOfLengthString, // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - //@httpHeader("X-Enum") + // @httpHeader("X-Enum") //enumStringHeader: EnumString, - //@httpHeader("X-Enum-List") - //enumStringListHeader: ListOfEnumString, + // @httpHeader("X-Enum-List") + // enumStringListHeader: ListOfEnumString, @httpQuery("lengthString") lengthStringQuery: LengthString, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index e0fe80bb45..2223a2cc37 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -22,10 +22,12 @@ import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecor import java.util.logging.Level import java.util.logging.Logger -/** Rust Codegen Plugin - * This is the entrypoint for code generation, triggered by the smithy-build plugin. - * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which - * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. +/** + * Rust Codegen Plugin + * + * This is the entrypoint for code generation, triggered by the smithy-build plugin. + * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which + * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. */ class RustCodegenServerPlugin : SmithyBuildPlugin { private val logger = Logger.getLogger(javaClass.name) @@ -48,8 +50,8 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { } companion object { - /** SymbolProvider - * When generating code, smithy types need to be converted into Rust types—that is the core role of the symbol provider + /** + * When generating code, smithy types need to be converted into Rust types—that is the core role of the symbol provider. * * The Symbol provider is composed of a base [SymbolVisitor] which handles the core functionality, then is layered * with other symbol providers, documented inline, to handle the full scope of Smithy types. @@ -58,11 +60,11 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig, - publicConstrainedShapesEnabled: Boolean = true + publicConstrainedTypesEnabled: Boolean = true ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs - .let { if (publicConstrainedShapesEnabled) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + .let { if (publicConstrainedTypesEnabled) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index ac1e9df5d3..6bab9474d3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -112,7 +112,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: protocolGeneratorFactory = generator model = generator.transformModel(codegenDecorator.transformModel(service, baseModel)) val baseProvider = RustCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) - // TODO No `CodegenDecorator` is altering the symbol provider, so we might as well remove the `symbolProvider` + // TODO Separate commit: No `CodegenDecorator` is altering the symbol provider, so we might as well remove the `symbolProvider` // method from the `RustCodegenDecorator` interface. symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) @@ -123,7 +123,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: model, service, symbolVisitorConfig, - publicConstrainedShapesEnabled = false + publicConstrainedTypesEnabled = false ) ) ), model, service diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index ec42b253b2..e9e9c3e7b7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -49,7 +49,6 @@ class ConstrainedStringGenerator( "length <= ${lengthTrait.max.get()}" } - // TODO Docs for `ConstraintViolation`. // TODO Display impl does not honor `sensitive` trait. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. @@ -115,7 +114,7 @@ class ConstrainedStringGenerator( """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, - "MaybeConstrained" to symbol.wrapMaybeConstrained(), + "MaybeConstrained" to symbol.makeMaybeConstrained(), "Display" to RuntimeType.Display, "TryFrom" to RuntimeType.TryFrom, ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 6719437af9..34ef78fa73 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -12,7 +12,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait // TODO Unit tests @@ -45,7 +45,7 @@ class ConstrainedTraitForEnumGenerator( } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "MaybeConstrained" to symbol.wrapMaybeConstrained(), + "MaybeConstrained" to symbol.makeMaybeConstrained(), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ecd9166567..fe30440fa5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -41,7 +41,7 @@ import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -53,7 +53,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - Always implements either From for Structure or TryFrom for Structure. // - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. class ServerBuilderGenerator( - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, private val shape: StructureShape, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider? = null, ) { @@ -67,6 +67,14 @@ class ServerBuilderGenerator( private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val codegenScope = arrayOf( + "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), + "Structure" to structureSymbol, + "From" to RuntimeType.From, + "TryFrom" to RuntimeType.TryFrom, + "MaybeConstrained" to RuntimeType.MaybeConstrained() + ) + fun render(writer: RustWriter) { writer.docs("See #D.", structureSymbol) writer.withModule(moduleName) { @@ -77,17 +85,15 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) - // TODO(): `#[non_exhaustive] unless we commit to making builders of builders public. writer.docs("Holds one variant for each of the ways the builder can fail.") Attribute.NonExhaustive.render(writer) - // TODO This should use `name` from the constraint violation symbol. - writer.rustBlock("pub enum ConstraintViolation") { + val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name + writer.rustBlock("pub enum $constraintViolationSymbolName") { constraintViolations().forEach { renderConstraintViolation(this, it) } } renderImplDisplayConstraintViolation(writer) - // TODO Use RuntimeType.StdError - writer.rust("impl std::error::Error for ConstraintViolation { }") + writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is // an operation input shape. @@ -127,9 +133,8 @@ class ServerBuilderGenerator( } // TODO This impl does not take into account sensitive trait. - // TODO Use RuntimeType.Display private fun renderImplDisplayConstraintViolation(writer: RustWriter) { - writer.rustBlock("impl std::fmt::Display for ConstraintViolation") { + writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { rustBlock("match self") { constraintViolations().forEach { @@ -146,30 +151,29 @@ class ServerBuilderGenerator( } private fun renderImplFromConstraintViolationForRequestRejection(writer: RustWriter) { - // TODO Use RuntimeType.From writer.rustTemplate( """ - impl From for #{RequestRejection} { + impl #{From} for #{RequestRejection} { fn from(value: ConstraintViolation) -> Self { Self::Build(value.into()) } } """, - "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig) + *codegenScope ) } private fun renderImplFromBuilderForMaybeConstrained(writer: RustWriter) { - // TODO Use RuntimeType.From - writer.rust( + writer.rustTemplate( """ - impl From for #{T} { + impl #{From} for #{StructureMaybeConstrained} { fn from(builder: Builder) -> Self { Self::Unconstrained(builder) } } """, - structureSymbol.wrapMaybeConstrained() + *codegenScope, + "StructureMaybeConstrained" to structureSymbol.makeMaybeConstrained() ) } @@ -206,7 +210,6 @@ class ServerBuilderGenerator( } } - // TODO(EventStream): [DX] Consider updating builders to take EventInputStream as Into private fun renderBuilderMember(writer: RustWriter, member: MemberShape) { val memberSymbol = builderMemberSymbol(member) val memberName = symbolProvider.toMemberName(member) @@ -243,7 +246,7 @@ class ServerBuilderGenerator( rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { - val maybeConstrainedConstrained = "${symbol.wrapMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + val maybeConstrainedConstrained = "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" @@ -298,7 +301,6 @@ class ServerBuilderGenerator( val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) - // TODO: `pub(crate)` unless we commit to making builders of builders public. // Setter names will never hit a reserved word and therefore never need escaping. writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { rust( @@ -341,7 +343,7 @@ class ServerBuilderGenerator( private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) = when (constraintViolation.kind) { ConstraintViolationKind.MISSING_MEMBER -> { - writer.docs("${constraintViolationMessage(constraintViolation).capitalize()}.") + writer.docs("${constraintViolationMessage(constraintViolation).replaceFirstChar { it.uppercase() }}.") writer.rust("${constraintViolation.name()},") } ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { @@ -357,7 +359,6 @@ class ServerBuilderGenerator( // Note we cannot express the inner constraint violation as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. writer.docs("${constraintViolationMessage(constraintViolation)}.") - // TODO: `#[doc(hidden)]` unless we commit to making builders of builders public. Attribute.DocHidden.render(writer) writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } @@ -397,7 +398,7 @@ class ServerBuilderGenerator( */ private fun builderMissingFieldForMember(member: MemberShape) = // TODO(https://github.com/awslabs/smithy-rs/issues/1302): We go through the symbol provider because - // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. + // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. if (symbolProvider.toSymbol(member).isOptional()) { null } else { @@ -415,22 +416,20 @@ class ServerBuilderGenerator( } } """, - "Structure" to structureSymbol, - "TryFrom" to RuntimeType.TryFrom, + *codegenScope ) } private fun renderFromBuilderImpl(writer: RustWriter) { - // TODO Use RuntimeType.From writer.rustTemplate( """ - impl From for #{Structure} { + impl #{From} for #{Structure} { fn from(builder: Builder) -> Self { builder.build() } } """, - "Structure" to structureSymbol, + *codegenScope ) } @@ -446,7 +445,6 @@ class ServerBuilderGenerator( pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } // Strip the `Option` in case the member is not `required`. - // TODO Grep for these and replace them with `extractSymbolFromOption`. .mapRustType { it.stripOuter() } val hadBox = strippedOption.isRustBoxed() @@ -455,7 +453,7 @@ class ServerBuilderGenerator( .mapRustType { it.stripOuter() } // Wrap it in the Cow-like `constrained::MaybeConstrained` type, since we know the target member shape can // reach a constrained shape. - .wrapMaybeConstrained() + .makeMaybeConstrained() // Box it in case the member can reach itself recursively. .letIf(hadBox) { it.makeRustBoxed() } // Ensure we always end up with an `Option`. @@ -507,7 +505,7 @@ class ServerBuilderGenerator( ) .transpose()? """, - "MaybeConstrained" to RuntimeType.MaybeConstrained() + *codegenScope ) } else { rustTemplate( @@ -522,7 +520,7 @@ class ServerBuilderGenerator( ) .transpose()? """, - "MaybeConstrained" to RuntimeType.MaybeConstrained() + *codegenScope ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 29549e50e7..6fd1242a70 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -31,8 +31,6 @@ class ServerEnumGenerator( private val unknownVariantSymbol = constraintViolationSymbolProvider.toSymbol(shape) override fun renderFromForStr() { - // TODO Docs for ConstraintViolation - // TODO Display for ConstraintViolation writer.rust( """ pub mod ${enumName.toSnakeCase()} { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 70cd2d7d1f..1752099769 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -13,10 +13,11 @@ import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs class UnconstrainedCollectionGenerator( @@ -42,7 +43,6 @@ class UnconstrainedCollectionGenerator( val constraintViolationName = constraintViolationSymbol.name val innerConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(innerShape) - // TODO Don't be lazy and don't use `Result<_, >`. unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( """ @@ -55,7 +55,7 @@ class UnconstrainedCollectionGenerator( } } - impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { + impl #{TryFrom}<$name> for #{ConstrainedSymbol} { type Error = #{ConstraintViolationSymbol}; fn try_from(value: $name) -> Result { @@ -73,7 +73,8 @@ class UnconstrainedCollectionGenerator( "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, "ConstrainedSymbol" to constrainedSymbol, "ConstraintViolationSymbol" to constraintViolationSymbol, - "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), + "TryFrom" to RuntimeType.TryFrom, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index c8979f6508..a70a02dfaa 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -6,11 +6,9 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape 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.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -24,7 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Docs @@ -54,17 +52,8 @@ class UnconstrainedMapGenerator( check(shape.canReachConstrainedShape(model, symbolProvider)) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() - // TODO The unconstrained shape symbol provider should always work. - val keySymbol = if (isKeyConstrained(keyShape)) { - unconstrainedShapeSymbolProvider.toSymbol(keyShape) - } else { - symbolProvider.toSymbol(keyShape) - } - val valueSymbol = if (isValueConstrained(valueShape)) { - unconstrainedShapeSymbolProvider.toSymbol(valueShape) - } else { - symbolProvider.toSymbol(valueShape) - } + val keySymbol = unconstrainedShapeSymbolProvider.toSymbol(keyShape) + val valueSymbol = unconstrainedShapeSymbolProvider.toSymbol(valueShape) unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( @@ -81,7 +70,7 @@ class UnconstrainedMapGenerator( """, "KeySymbol" to keySymbol, "ValueSymbol" to valueSymbol, - "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), ) renderTryFromUnconstrainedForConstrained(this) @@ -90,8 +79,6 @@ class UnconstrainedMapGenerator( renderConstraintViolation() } - // TODO Docs for ConstraintViolation. - // TODO KeyConstraintViolation and ValueConstraintViolation need to be `#[doc(hidden)]`. private fun renderConstraintViolation() { val constraintViolationCodegenScope = listOfNotNull( if (isKeyConstrained(keyShape)) { @@ -114,8 +101,8 @@ class UnconstrainedMapGenerator( ##[derive(Debug, PartialEq)] pub enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} - ${if (isKeyConstrained(keyShape)) "Key(#{KeyConstraintViolationSymbol})," else ""} - ${if (isValueConstrained(valueShape)) "Value(#{ValueConstraintViolationSymbol})," else ""} + ${if (isKeyConstrained(keyShape)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} + ${if (isValueConstrained(valueShape)) "##[doc(hidden)] Value(#{ValueConstraintViolationSymbol})," else ""} } """, *constraintViolationCodegenScope, @@ -206,12 +193,5 @@ class UnconstrainedMapGenerator( private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) - private fun isValueConstrained(shape: Shape): Boolean = when (shape) { - is StructureShape -> shape.canReachConstrainedShape(model, symbolProvider) - is CollectionShape -> shape.canReachConstrainedShape(model, symbolProvider) - is MapShape -> shape.canReachConstrainedShape(model, symbolProvider) - is StringShape -> shape.isDirectlyConstrained(symbolProvider) - // TODO(https://github.com/awslabs/smithy-rs/pull/1199) Other constraint traits on simple shapes. - else -> false - } + private fun isValueConstrained(shape: Shape): Boolean = shape.canReachConstrainedShape(model, symbolProvider) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 52f01c31d4..df8e492793 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -29,7 +29,7 @@ import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.wrapMaybeConstrained +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase @@ -74,7 +74,7 @@ class UnconstrainedUnionGenerator( rustTemplate( """ - impl std::convert::TryFrom<$name> for #{ConstrainedSymbol} { + impl #{TryFrom}<$name> for #{ConstrainedSymbol} { type Error = #{ConstraintViolationSymbol}; fn try_from(value: $name) -> Result { @@ -82,6 +82,7 @@ class UnconstrainedUnionGenerator( } } """, + "TryFrom" to RuntimeType.TryFrom, "ConstrainedSymbol" to constrainedSymbol, "ConstraintViolationSymbol" to constraintViolationSymbol, "body" to generateTryFromUnconstrainedUnionImpl(), @@ -101,7 +102,7 @@ class UnconstrainedUnionGenerator( } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "MaybeConstrained" to constrainedSymbol.wrapMaybeConstrained(), + "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "ConstrainedSymbol" to constrainedSymbol, "UnconstrainedSymbol" to symbol ) @@ -125,7 +126,6 @@ class UnconstrainedUnionGenerator( .filter { it.targetCanReachConstrainedShape(model, symbolProvider) } .map { ConstraintViolation(it) } - // TODO I expect we can deduplicate a bit of code; this was copied from [ServerBuilderGenerator.renderConstraintViolation]. private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) { val targetShape = model.expectShape(constraintViolation.forMember.target) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 4450703e25..78cc5dafd8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -111,6 +111,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) : ProtocolTraitImplGenerator { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider + // TODO Use `!!` here and not everywhere else. Or figure out a way so that clients and servers can have different `CodegenContext` types. private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig @@ -871,7 +872,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( if (queryParamsBinding != null) { val target = model.expectShape(queryParamsBinding.member.target, MapShape::class.java) val hasConstrainedTarget = target.canReachConstrainedShape(model, symbolProvider) - // TODO Here we only check the target shape; constraint traits on member shapes are not implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Here we only check the target shape; + // constraint traits on member shapes are not implemented yet. val targetSymbol = unconstrainedShapeSymbolProvider!!.toSymbol(target) withBlock("let mut query_params: #T = ", ";", targetSymbol) { conditionalBlock("#T(", ")", conditional = hasConstrainedTarget, targetSymbol) { @@ -945,7 +947,8 @@ private class ServerHttpBoundProtocolTraitImplGenerator( if (queryParamsBinding != null) { val target = model.expectShape(queryParamsBinding.member.target, MapShape::class.java) - // TODO Here we only check the target shape; constraint traits on member shapes are not implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Here we only check the target shape; + // constraint traits on member shapes are not implemented yet. val hasConstrainedTarget = target.canReachConstrainedShape(model, symbolProvider) when (queryParamsBinding.queryParamsBindingTargetMapValueType()) { QueryParamsTargetMapValueType.STRING -> { @@ -977,12 +980,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } if (queryParamsBinding != null) { - // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. - val hasConstrainedTarget = - model.expectShape(queryParamsBinding.member.target, MapShape::class.java).canReachConstrainedShape(model, symbolProvider) - // TODO Why not always use the unconstrainedShapeSymbolProvider? It should work! - val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider - val isOptional = symbolProvider.toSymbol(queryParamsBinding.member).isOptional() + val isOptional = unconstrainedShapeSymbolProvider!!.toSymbol(queryParamsBinding.member).isOptional() withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { write("query_params") @@ -990,21 +988,28 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } queryBindingsTargettingCollection.forEach { binding -> - // TODO Constraint traits on member shapes are not implemented yet. We would have to check those here too. - // TODO UnconstrainedShapeSymbolProvider should always work. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not + // implemented yet. val hasConstrainedTarget = model.expectShape(binding.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) - val symbolProvider = if (hasConstrainedTarget) unconstrainedShapeSymbolProvider!! else symbolProvider - val memberName = symbolProvider.toMemberName(binding.member) - val isOptional = symbolProvider.toSymbol(binding.member).isOptional() + val memberName = unconstrainedShapeSymbolProvider!!.toMemberName(binding.member) + val isOptional = unconstrainedShapeSymbolProvider.toSymbol(binding.member).isOptional() rustBlock("if !$memberName.is_empty()") { - withBlock("input = input.${binding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}(", ");") { + withBlock( + "input = input.${ + binding.member.deserializerBuilderSetterName( + model, + unconstrainedShapeSymbolProvider, + codegenContext.target + ) + }(", ");" + ) { conditionalBlock("Some(", ")", conditional = isOptional) { conditionalBlock( "#T(", ")", conditional = hasConstrainedTarget, - symbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }) { + unconstrainedShapeSymbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }) { write(memberName) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 5f1727e791..ae60275ccc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -46,8 +46,9 @@ fun serverTestCodegenContext( model, serverTestSymbolProvider(model), TestRuntimeConfig, - // TODO(https://github.com/awslabs/smithy-rs/pull/1340) We should not fabricate a service shape out of thin air here, but rather look it up in the model. - serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), + serviceShape + ?: model.serviceShapes.firstOrNull() + ?: ServiceShape.builder().version("test").id("test#Service").build(), ShapeId.from("test#Protocol"), settings, codegenTarget diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 881fe05b97..7229c625a8 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -53,13 +53,12 @@ class ServerEnumGeneratorTest { shape, shape.expectTrait() ).render() - // TODO Last line needs to be replaced with the `ConstraintViolation`. writer.compileAndTest( """ use std::str::FromStr; assert_eq!(InstanceType::try_from("t2.nano").unwrap(), InstanceType::T2Nano); assert_eq!(InstanceType::from_str("t2.nano").unwrap(), InstanceType::T2Nano); - //assert_eq!(InstanceType::try_from("unknown").unwrap_err(), InstanceTypeUnknownVariantError("unknown".to_string())); + assert_eq!(InstanceType::try_from("unknown").unwrap_err(), crate::model::instance_type::ConstraintViolation(String::from("unknown"))); """ ) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index cf10c5f922..7d33ce88e9 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -127,23 +127,29 @@ fun Symbol.makeRustBoxed(): Symbol = .build() } -// TODO This can be written in terms of `mapRustType`. -// TODO isMaybeConstrained. Make it like the others. -fun Symbol.wrapMaybeConstrained(): Symbol { - val rustType = RustType.MaybeConstrained(this.rustType()) - return Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() -} +/** + * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). + * + * This is idempotent and will have no change if the type is already `MaybeConstrained`. + */ +fun Symbol.makeMaybeConstrained(): Symbol = + if (this.rustType() is RustType.MaybeConstrained) { + this + } else { + val rustType = RustType.MaybeConstrained(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } /** Map the RustType of a symbol with [f] */ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { val newType = f(this.rustType()) return Symbol.builder() .rustType(newType) - // TODO This is a bug. Grep for all `addReference(this)`, maybe they are too. + // TODO Separate commit: This is a bug if `f` swaps the type instead of wrapping it. .addReference(this) .name(newType.name) .build() @@ -440,11 +446,6 @@ fun Symbol.isOptional(): Boolean = when (this.rustType()) { else -> false } -/** - * Get the referenced symbol for T if [this] is an Option, [this] otherwise - */ -fun Symbol.extractSymbolFromOption(): Symbol = this.mapRustType { it.stripOuter() } - fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter() is RustType.Box // Symbols should _always_ be created with a Rust type & shape attached diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index 4f42c624fa..d7cb42f19c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -137,7 +137,7 @@ class UnconstrainedShapeSymbolProvider( base.toSymbol(shape) } // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not - // implemented yet. + // implemented yet. } is StringShape -> { if (shape.canReachConstrainedShape(model, base)) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt index a5d34b40fa..4d8b83ac54 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt @@ -75,8 +75,8 @@ class ErrorGenerator( val symbol = symbolProvider.toSymbol(shape) val messageShape = shape.errorMessageMember() val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) - // TODO Why do we always generate a `pub fn message() -> Option<&str> { None }` for `@error` structure shapes, - // even when they don’t have a `message` field? + // TODO Separate commit: Why do we always generate a `pub fn message() -> Option<&str> { None }` for `@error` structure shapes, + // even when they don’t have a `message` field? val (returnType, message) = messageShape?.let { val messageSymbol = symbolProvider.toSymbol(it).mapRustType { t -> t.asDeref() } if (messageSymbol.rustType().stripOuter() is RustType.Opaque) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 27019eb5f6..d1d80e398a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -552,12 +552,9 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> val listHeader = memberType is CollectionShape - // TODO This should be a method. - val workingWithPublicConstrainedWrapperTupleType = - codegenTarget == CodegenTarget.SERVER && memberShape.hasPublicConstrainedWrapperTupleType(model) rustTemplate( """ - for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType) "&$field.0" else field } { + for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType(memberShape)) "&$field.0" else field } { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} @@ -608,9 +605,7 @@ class HttpBindingGenerator( // `httpPrefixHeaders` trait was found, and _not_ to the collection member shape on which we'd have // to check for constraint trait precedence. So `member.hasPublicConstrainedWrapperTupleType()` is // _not_ what we want. - val workingWithPublicConstrainedWrapperTupleType = - codegenTarget == CodegenTarget.SERVER && target.hasPublicConstrainedWrapperTupleType(model) - quoteValue("AsRef::::as_ref(${ if (workingWithPublicConstrainedWrapperTupleType) "&$targetName.0" else targetName })") + quoteValue("AsRef::::as_ref(${if (workingWithPublicConstrainedWrapperTupleType(target)) "&$targetName.0" else targetName})") } } target.isTimestampShape -> { @@ -628,4 +623,7 @@ class HttpBindingGenerator( else -> throw CodegenException("unexpected shape: $target") } } + + private fun workingWithPublicConstrainedWrapperTupleType(shape: Shape) = + codegenTarget == CodegenTarget.SERVER && shape.hasPublicConstrainedWrapperTupleType(model) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 6635ef4ac0..bb7d9f1dfd 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -58,7 +58,7 @@ import software.amazon.smithy.rust.codegen.util.outputShape import software.amazon.smithy.utils.StringUtils // TODO: Separate commit: Make all functions pub(crate). If the functions have in their type signature a pub(crate) type, -// and the function is declared `pub`, Rust will complain, even if the json_deser module is not `pub`. +// and the function is declared `pub`, Rust will complain, even if the json_deser module is not `pub`. class JsonParserGenerator( codegenContext: CodegenContext, @@ -214,26 +214,38 @@ class JsonParserGenerator( rustBlock("match key.to_unescaped()?.as_ref()") { for (member in members) { rustBlock("${jsonName(member).dq()} =>") { - // TODO Use when - if (codegenTarget == CodegenTarget.CLIENT) { - withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget)}(", ");") { - deserializeMember(member) - } - } else { - if (symbolProvider.toSymbol(member).isOptional()) { - withBlock("builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget)}(", ");") { + when (codegenTarget) { + CodegenTarget.CLIENT -> { + withBlock( + "builder = builder.${ + member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + }(", ");" + ) { deserializeMember(member) } - } else { - rust("if let Some(v) = ") - deserializeMember(member) - rust( - """ - { - builder = builder.${member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget)}(v); + } + CodegenTarget.SERVER -> { + if (symbolProvider.toSymbol(member).isOptional()) { + withBlock( + "builder = builder.${ + member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + }(", ");" + ) { + deserializeMember(member) } - """ - ) + } else { + rust("if let Some(v) = ") + deserializeMember(member) + rust( + """ + { + builder = builder.${ + member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + }(v); + } + """ + ) + } } } } @@ -271,7 +283,7 @@ class JsonParserGenerator( withBlock("$escapedStrName.to_unescaped().map(|u|", ")") { when (target.hasTrait()) { true -> { - if (parseUnconstrainedEnum(target)) { + if (parseUnconstrainedShape(target)) { rust("u.into_owned()") } else { rust("#T::from(u.as_ref())", symbolProvider.toSymbol(target)) @@ -282,8 +294,6 @@ class JsonParserGenerator( } } - private fun parseUnconstrainedEnum(shape: StringShape) = codegenTarget == CodegenTarget.SERVER && shape.hasTrait() - private fun RustWriter.deserializeString(target: StringShape) { withBlockTemplate("#{expect_string_or_null}(tokens.next())?.map(|s|", ").transpose()?", *codegenScope) { deserializeStringInner(target, "s") @@ -308,7 +318,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeCollection(shape: CollectionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) + val returnUnconstrainedType = parseUnconstrainedShape(shape) val returnType = if (returnUnconstrainedType) { unconstrainedShapeSymbolProvider.toSymbol(shape) } else { @@ -364,7 +374,7 @@ class JsonParserGenerator( val keyTarget = model.expectShape(shape.key.target) as StringShape val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) + val returnUnconstrainedType = parseUnconstrainedShape(shape) val returnType = if (returnUnconstrainedType) { unconstrainedShapeSymbolProvider.toSymbol(shape) } else { @@ -437,18 +447,6 @@ class JsonParserGenerator( rust("Ok(Some(builder))") } else { rust("Ok(Some(builder.build()))") -// withBlock("Ok(Some(builder.build()", "))") { -// if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { -// rustTemplate( -// """ -// .map_err(|err| #{Error}::new( -// #{ErrorReason}::Custom(format!("{}", err).into()), None) -// )? -// """, -// *codegenScope -// ) -// } -// } } } } @@ -456,9 +454,6 @@ class JsonParserGenerator( rust("#T(tokens)?", nestedParser) } - // TODO Grep for `returnUnconstrainedType` and `parseUnconstrainedEnum`. They should _all_ use this method. - private fun parseUnconstrainedShape(shape: Shape) = codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) - private fun RustWriter.deserializeUnion(shape: UnionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = if (parseUnconstrainedShape(shape)) { @@ -591,4 +586,13 @@ class JsonParserGenerator( } } } + + /** + * Whether we should parse a value for a shape into its associated unconstrained type. For example, when the shape + * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the + * user gets. This is only relevant for the server, that parses the incoming request and only after enforces + * constraint traits. + */ + private fun parseUnconstrainedShape(shape: Shape) = + codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt index 241789ea15..060b318282 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt @@ -304,7 +304,7 @@ object EventStreamTestModels { """.trimIndent(), ) { Ec2QueryProtocol(it) }, ) - // TODO This is wrong: server tests should be run from the server subproject, and use `serverTestSymbolProvider()` + // TODO Separate commit: This is wrong: server tests should be run from the server subproject, and use `serverTestSymbolProvider()` // .flatMap { listOf(it, it.copy(target = CodegenTarget.SERVER)) } class UnmarshallTestCasesProvider : ArgumentsProvider { From 0983546392a45a8a91f861b67a1fca199da8f74a Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 2 Jun 2022 11:13:22 +0200 Subject: [PATCH 115/255] Add TODO about pulling out type_complexity to a separate commit --- .../smithy/rust/codegen/smithy/generators/LibRsGenerator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt index 9f85d7c49d..7270277342 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt @@ -44,6 +44,7 @@ class LibRsGenerator( rust("##![warn(missing_docs)]") } + // TODO: Separate commit rust("##![allow(clippy::type_complexity)]") val libraryDocs = settings.getService(model).getTrait()?.value ?: settings.moduleName From ea1bbca4d4ba1b4f4b74b87d8e8fa3a6b9ed7eb1 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 3 Jun 2022 12:15:14 +0200 Subject: [PATCH 116/255] Assorted fixes for constrained unions --- .../smithy/generators/ServerEnumGenerator.kt | 8 +++--- .../generators/UnconstrainedUnionGenerator.kt | 27 ++++++++++++------- .../ServerHttpBoundProtocolGenerator.kt | 8 +++++- .../customizations/AllowLintsGenerator.kt | 3 +++ .../codegen/smithy/generators/Instantiator.kt | 7 ++++- .../smithy/generators/LibRsGenerator.kt | 3 --- .../generators/http/HttpBindingGenerator.kt | 6 ++++- .../protocols/parse/JsonParserGenerator.kt | 4 ++- 8 files changed, 45 insertions(+), 21 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 6fd1242a70..7ac16272eb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -41,7 +41,7 @@ class ServerEnumGenerator( ) writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { rust("type Error = #T;", unknownVariantSymbol) - rustBlock("fn try_from(s: &str) -> Result") { + rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { rustBlock("match s") { sortedMembers.forEach { member -> rust("${member.value.dq()} => Ok($enumName::${member.derivedName()}),") @@ -54,8 +54,8 @@ class ServerEnumGenerator( """ impl #{TryFrom} for $enumName { type Error = #{UnknownVariantSymbol}; - fn try_from(s: String) -> Result { - s.try_into() + fn try_from(s: String) -> std::result::Result>::Error> { + s.as_str().try_into() } } """, @@ -70,7 +70,7 @@ class ServerEnumGenerator( impl std::str::FromStr for $enumName { type Err = #{UnknownVariantSymbol}; - fn from_str(s: &str) -> std::result::Result { + fn from_str(s: &str) -> std::result::Result::Err> { Self::try_from(s) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index df8e492793..cf7c639378 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -27,9 +27,9 @@ import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvid import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf +import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase @@ -60,6 +60,7 @@ class UnconstrainedUnionGenerator( unconstrainedModuleWriter.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustBlock( """ + ##[allow(clippy::enum_variant_names)] ##[derive(Debug, Clone)] pub(crate) enum $name """ @@ -148,25 +149,30 @@ class UnconstrainedUnionGenerator( sortedMembers.forEach { member -> val memberName = unconstrainedShapeSymbolProvider.toMemberName(member) withBlockTemplate( - "#{UnconstrainedUnion}::$memberName(unconstrained) => Self::$memberName(", - "),", + "#{UnconstrainedUnion}::$memberName(unconstrained) => Self::$memberName({", + "}),", "UnconstrainedUnion" to symbol, ) { if (member.targetCanReachConstrainedShape(model, symbolProvider)) { val targetShape = model.expectShape(member.target) val resolveToNonPublicConstrainedType = !targetShape.isDirectlyConstrained(symbolProvider) && - !targetShape.isStructureShape + !targetShape.isStructureShape && + !targetShape.isUnionShape + + val hasBox = member.hasTrait() + if (hasBox) { + rust("let unconstrained = *unconstrained;") + } if (resolveToNonPublicConstrainedType) { rustTemplate( """ - { - let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained - .try_into() - .map_err(Self::Error::${ConstraintViolation(member).name()})?; - constrained.into() - } + let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained + .try_into() + ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } + .map_err(Self::Error::${ConstraintViolation(member).name()})?; + constrained.into() """, "PubCrateConstrainedShapeSymbol" to pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) ) @@ -175,6 +181,7 @@ class UnconstrainedUnionGenerator( """ unconstrained .try_into() + ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } .map_err(Self::Error::${ConstraintViolation(member).name()})? """ ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 78cc5dafd8..f936b1a82f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -913,7 +913,13 @@ private class ServerHttpBoundProtocolTraitImplGenerator( when { memberShape.isStringShape -> { - rust("let v = v.into_owned();") + if (queryParamsBinding != null) { + // If there's an `@httpQueryParams` binding, it will want to consume the parsed data + // too further down, so we need to clone it. + rust("let v = v.clone().into_owned();") + } else { + rust("let v = v.into_owned();") + } } memberShape.isTimestampShape -> { val index = HttpBindingIndex.of(model) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt index 8c758ec098..b29a9a2094 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt @@ -31,6 +31,9 @@ val ClippyAllowLints = listOf( // Forcing use of `vec![]` can make codegen harder in some cases "vec_init_then_push", + + // TODO: Separate commit + "type_complexity" ) val AllowDocsLints = listOf( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index 133fceb327..3bb152d0ca 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -45,6 +45,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.expectMember import software.amazon.smithy.rust.codegen.util.hasTrait @@ -288,7 +289,11 @@ class Instantiator( writer.write(".build()") val hasFallibleBuilder = when (codegenTarget) { CodegenTarget.CLIENT -> StructureGenerator.hasFallibleBuilder(shape, symbolProvider) - CodegenTarget.SERVER -> StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes = false) + CodegenTarget.SERVER -> { + // Only operation input builders take in unconstrained types. + val takesInUnconstrainedTypes = shape.hasTrait() + StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takesInUnconstrainedTypes) + } } if (hasFallibleBuilder) { writer.write(".unwrap()") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt index 7270277342..b16a581650 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/LibRsGenerator.kt @@ -44,9 +44,6 @@ class LibRsGenerator( rust("##![warn(missing_docs)]") } - // TODO: Separate commit - rust("##![allow(clippy::type_complexity)]") - val libraryDocs = settings.getService(model).getTrait()?.value ?: settings.moduleName containerDocs(escape(libraryDocs)) val crateLayout = customizations.map { it.section(LibRsSection.ModuleDocumentation(LibRsSection.CrateOrganization)) }.filter { !it.isEmpty() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index d1d80e398a..8f6db33a38 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -412,7 +412,11 @@ class HttpBindingGenerator( if (returnUnconstrainedType) { rust( """ - Ok(Some(#T($parsedValue))) + Ok(if !$parsedValue.is_empty() { + Some(#T($parsedValue)) + } else { + None + }) """, symbolProvider.toSymbol(targetShape) ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index bb7d9f1dfd..5e3400f44e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -271,7 +271,9 @@ class JsonParserGenerator( } val symbol = symbolProvider.toSymbol(memberShape) if (symbol.isRustBoxed()) { - if (codegenTarget == CodegenTarget.SERVER && memberShape.targetCanReachConstrainedShape(model, symbolProvider)) { + if (codegenTarget == CodegenTarget.SERVER && + model.expectShape(memberShape.container).isStructureShape && + memberShape.targetCanReachConstrainedShape(model, symbolProvider)) { // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. rust(".map(|x| x.into())") } From 7829ddc835c1fc6aed5867eb146a80098353cb7a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 14:35:23 +0200 Subject: [PATCH 117/255] Add links to PRs/issues for some TODOs --- .../amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt | 3 ++- .../rust/codegen/smithy/customizations/AllowLintsGenerator.kt | 2 +- .../rust/codegen/smithy/generators/error/ErrorGenerator.kt | 2 +- .../rust/codegen/smithy/protocols/EventStreamTestTools.kt | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 7d33ce88e9..01db052a91 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -149,7 +149,8 @@ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol { val newType = f(this.rustType()) return Symbol.builder() .rustType(newType) - // TODO Separate commit: This is a bug if `f` swaps the type instead of wrapping it. + // TODO(https://github.com/awslabs/smithy-rs/pull/1438) Separate + // commit: This is a bug if `f` swaps the type instead of wrapping it. .addReference(this) .name(newType.name) .build() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt index b29a9a2094..39b2de482a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt @@ -32,7 +32,7 @@ val ClippyAllowLints = listOf( // Forcing use of `vec![]` can make codegen harder in some cases "vec_init_then_push", - // TODO: Separate commit + // TODO(https://github.com/awslabs/smithy-rs/pull/1440): Separate commit. "type_complexity" ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt index 4d8b83ac54..78d7920335 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt @@ -75,7 +75,7 @@ class ErrorGenerator( val symbol = symbolProvider.toSymbol(shape) val messageShape = shape.errorMessageMember() val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) - // TODO Separate commit: Why do we always generate a `pub fn message() -> Option<&str> { None }` for `@error` structure shapes, + // TODO(https://github.com/awslabs/smithy-rs/pull/1441) Separate commit: Why do we always generate a `pub fn message() -> Option<&str> { None }` for `@error` structure shapes, // even when they don’t have a `message` field? val (returnType, message) = messageShape?.let { val messageSymbol = symbolProvider.toSymbol(it).mapRustType { t -> t.asDeref() } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt index 060b318282..6aca75d6cf 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/EventStreamTestTools.kt @@ -304,7 +304,9 @@ object EventStreamTestModels { """.trimIndent(), ) { Ec2QueryProtocol(it) }, ) - // TODO Separate commit: This is wrong: server tests should be run from the server subproject, and use `serverTestSymbolProvider()` + // TODO(https://github.com/awslabs/smithy-rs/issues/1442) Server tests + // should be run from the server subproject using the + // `serverTestSymbolProvider()`. // .flatMap { listOf(it, it.copy(target = CodegenTarget.SERVER)) } class UnmarshallTestCasesProvider : ArgumentsProvider { From e280a013945791e451de4609f4028c6c2f4bdc83 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 17:01:03 +0200 Subject: [PATCH 118/255] save work --- .../ServerHttpBoundProtocolGenerator.kt | 15 ++++++------ .../codegen/server/smithy/ConstraintsTest.kt | 2 +- .../rust/codegen/smithy/CodegenContext.kt | 24 +++++++++++++++---- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index f936b1a82f..ec9dc6fee0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -111,8 +111,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) : ProtocolTraitImplGenerator { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider - // TODO Use `!!` here and not everywhere else. Or figure out a way so that clients and servers can have different `CodegenContext` types. - private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider!! private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver @@ -616,7 +615,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val httpBindingGenerator = ServerRequestBindingGenerator( protocol, codegenContext, - codegenContext.unconstrainedShapeSymbolProvider!!, + unconstrainedShapeSymbolProvider, operationShape ) val structuredDataParser = protocol.structuredDataParser(operationShape) @@ -874,7 +873,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val hasConstrainedTarget = target.canReachConstrainedShape(model, symbolProvider) // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Here we only check the target shape; // constraint traits on member shapes are not implemented yet. - val targetSymbol = unconstrainedShapeSymbolProvider!!.toSymbol(target) + val targetSymbol = unconstrainedShapeSymbolProvider.toSymbol(target) withBlock("let mut query_params: #T = ", ";", targetSymbol) { conditionalBlock("#T(", ")", conditional = hasConstrainedTarget, targetSymbol) { rust("#T::new()", RustType.HashMap.RuntimeType) @@ -963,7 +962,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( QueryParamsTargetMapValueType.LIST, QueryParamsTargetMapValueType.SET -> { if (hasConstrainedTarget) { val collectionShape = model.expectShape(target.value.target, CollectionShape::class.java) - val collectionSymbol = unconstrainedShapeSymbolProvider!!.toSymbol(collectionShape) + val collectionSymbol = unconstrainedShapeSymbolProvider.toSymbol(collectionShape) rust( // `or_insert_with` instead of `or_insert` to avoid the allocation when the entry is // not empty. @@ -986,7 +985,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } } if (queryParamsBinding != null) { - val isOptional = unconstrainedShapeSymbolProvider!!.toSymbol(queryParamsBinding.member).isOptional() + val isOptional = unconstrainedShapeSymbolProvider.toSymbol(queryParamsBinding.member).isOptional() withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { write("query_params") @@ -998,7 +997,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( // implemented yet. val hasConstrainedTarget = model.expectShape(binding.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) - val memberName = unconstrainedShapeSymbolProvider!!.toMemberName(binding.member) + val memberName = unconstrainedShapeSymbolProvider.toMemberName(binding.member) val isOptional = unconstrainedShapeSymbolProvider.toSymbol(binding.member).isOptional() rustBlock("if !$memberName.is_empty()") { withBlock( @@ -1064,7 +1063,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun generateParseStrFn(binding: HttpBindingDescriptor, percentDecoding: Boolean): RuntimeType { - val output = unconstrainedShapeSymbolProvider!!.toSymbol(binding.member) + val output = unconstrainedShapeSymbolProvider.toSymbol(binding.member) val fnName = generateParseStrFnName(binding) return RuntimeType.forInlineFun(fnName, operationDeserModule) { writer -> writer.rustBlockTemplate( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index fcfe28ca06..dde4f43192 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -109,7 +109,7 @@ class ConstraintsTest { @Test fun `it should detect supported constrained traits as constrained`() { - listOf(mapA, structA, lengthString).forEach { + listOf(mapA, structA, lengthString, myString).forEach { // TODO When a test like this one fails, we get: // ``` // io.kotest.assertions.AssertionFailedError: expected: but was: diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt index b2059a10ba..92bc971089 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget /** - * Configuration needed to generate the client for a given Service<->Protocol pair + * Configuration needed to generate the client or the server for a given (Service, Protocol) pair. */ data class CodegenContext( /** @@ -21,7 +21,25 @@ data class CodegenContext( * an entry point. */ val model: Model, + val symbolProvider: RustSymbolProvider, + + /** + * This is nullable as only the server needs it. + * + * TODO: I think the time has come for us to have separate classes: + * - A `ClientCodegenContext` holding `RustSettings`. + * - A `ServerCodegenContext` holding `ServerRustSettings`, `UnconstrainedShapeSymbolProvider`. + * - A `CoreCodegenContext` held by the two classes above for the common properties. + * This "split" would also happen in: + * - `RustSettings`: `ClientRustSettings`, `ServerRustSettings`, `CoreRustSettings`. + * - `CodegenConfig`: `ClientCodegenConfig`, `ServerCodegenConfig`, `CoreCodegenConfig`. + * This would mean generators will only be able to rely on the `Core*` classes: + * - if they just need to know who they're generating for, `CodegenTarget` should be passed in separately. + * - if they additionally need other things that pertain only to the client or only to the server, they + * should be passed in separately in a bigger enum-like class encapsulating it. + */ + val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null, /** * Configuration of the runtime package: * - Where are the runtime crates (smithy-*) located on the file system? Or are they versioned? @@ -46,8 +64,6 @@ data class CodegenContext( * Some settings are dependent on whether server vs. client codegen is being invoked. */ val target: CodegenTarget, - - val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null ) { constructor( model: Model, @@ -57,7 +73,7 @@ data class CodegenContext( settings: RustSettings, target: CodegenTarget, unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null - ) : this(model, symbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, target, unconstrainedShapeSymbolProvider) + ) : this(model, symbolProvider, unconstrainedShapeSymbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, target) /** * The name of the cargo crate to generate e.g. `aws-sdk-s3` From f2e3b27bbb0e215a0d1efa0bd94a929a18d10a90 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 17:52:02 +0200 Subject: [PATCH 119/255] Use io.kotest.inspectors.forAll --- .../codegen/server/smithy/ConstraintsTest.kt | 11 ++---- .../rust/codegen/smithy/CodegenContext.kt | 35 ++++++++++--------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index dde4f43192..9e6731154a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.server.smithy +import io.kotest.inspectors.forAll import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape @@ -109,20 +110,14 @@ class ConstraintsTest { @Test fun `it should detect supported constrained traits as constrained`() { - listOf(mapA, structA, lengthString, myString).forEach { - // TODO When a test like this one fails, we get: - // ``` - // io.kotest.assertions.AssertionFailedError: expected: but was: - // at software.amazon.smithy.rust.codegen.server.smithy.ConstraintsTest.it should detect supported constrained traits as constrained(ConstraintsTest.kt:109) - // ``` - // How can I make it so that it prints `it` to determine which particular case failed? + listOf(mapA, structA, lengthString).forAll { it.isDirectlyConstrained(symbolProvider) shouldBe true } } @Test fun `it should not detect unsupported constrained traits as constrained`() { - listOf(structAInt, structAString, myString).forEach { + listOf(structAInt, structAString, myString).forAll { it.isDirectlyConstrained(symbolProvider) shouldBe false } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt index 92bc971089..ac108a9d00 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt @@ -24,22 +24,6 @@ data class CodegenContext( val symbolProvider: RustSymbolProvider, - /** - * This is nullable as only the server needs it. - * - * TODO: I think the time has come for us to have separate classes: - * - A `ClientCodegenContext` holding `RustSettings`. - * - A `ServerCodegenContext` holding `ServerRustSettings`, `UnconstrainedShapeSymbolProvider`. - * - A `CoreCodegenContext` held by the two classes above for the common properties. - * This "split" would also happen in: - * - `RustSettings`: `ClientRustSettings`, `ServerRustSettings`, `CoreRustSettings`. - * - `CodegenConfig`: `ClientCodegenConfig`, `ServerCodegenConfig`, `CoreCodegenConfig`. - * This would mean generators will only be able to rely on the `Core*` classes: - * - if they just need to know who they're generating for, `CodegenTarget` should be passed in separately. - * - if they additionally need other things that pertain only to the client or only to the server, they - * should be passed in separately in a bigger enum-like class encapsulating it. - */ - val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null, /** * Configuration of the runtime package: * - Where are the runtime crates (smithy-*) located on the file system? Or are they versioned? @@ -64,6 +48,23 @@ data class CodegenContext( * Some settings are dependent on whether server vs. client codegen is being invoked. */ val target: CodegenTarget, + + /** + * This is nullable as only the server needs it. + * + * TODO: I think the time has come for us to have separate classes: + * - A `ClientCodegenContext` holding `RustSettings`. + * - A `ServerCodegenContext` holding `ServerRustSettings`, `UnconstrainedShapeSymbolProvider`. + * - A `CoreCodegenContext` held by the two classes above for the common properties. + * This "split" would also happen in: + * - `RustSettings`: `ClientRustSettings`, `ServerRustSettings`, `CoreRustSettings`. + * - `CodegenConfig`: `ClientCodegenConfig`, `ServerCodegenConfig`, `CoreCodegenConfig`. + * This would mean generators will only be able to rely on the `Core*` classes: + * - if they just need to know who they're generating for, `CodegenTarget` should be passed in separately. + * - if they additionally need other things that pertain only to the client or only to the server, they + * should be passed in separately in a bigger enum-like class encapsulating it. + */ + val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null, ) { constructor( model: Model, @@ -73,7 +74,7 @@ data class CodegenContext( settings: RustSettings, target: CodegenTarget, unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null - ) : this(model, symbolProvider, unconstrainedShapeSymbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, target) + ) : this(model, symbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, target, unconstrainedShapeSymbolProvider) /** * The name of the cargo crate to generate e.g. `aws-sdk-s3` From b20e5c4a6f2f261e6533dfa3c10002242ff4bce4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 19:46:01 +0200 Subject: [PATCH 120/255] Unit tests for ConstrainedShapeSymbolProvider and PubCrateConstrainedShapeSymbolProvider --- .../smithy/ConstrainedShapeSymbolProvider.kt | 7 +- .../ConstraintViolationSymbolProvider.kt | 1 - .../smithy/testutil/ServerTestHelpers.kt | 9 +- .../ConstrainedShapeSymbolProviderTest.kt | 98 +++++++++++++++ ...CrateConstrainedShapeSymbolProviderTest.kt | 115 ++++++++++++++++++ .../UnconstrainedShapeSymbolProviderTest.kt | 14 +-- .../PubCrateConstrainedShapeSymbolProvider.kt | 1 - 7 files changed, 230 insertions(+), 15 deletions(-) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index e06ff1e563..f1931f6f2e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -29,7 +29,6 @@ import software.amazon.smithy.rust.codegen.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase -// TODO Unit tests. /** * The [ConstrainedShapeSymbolProvider] returns, for a given _directly_ * constrained shape, a symbol whose Rust type can hold the constrained values. @@ -56,8 +55,8 @@ class ConstrainedShapeSymbolProvider( ) : WrappingSymbolProvider(base) { private val nullableIndex = NullableIndex.of(model) - private fun publicConstrainedSymbolForCollectionOrMapShape(shape: Shape): Symbol { - check(shape is CollectionShape || shape is MapShape) + private fun publicConstrainedSymbolForMapShape(shape: Shape): Symbol { + check(shape is MapShape) val rustType = RustType.Opaque(shape.contextName(serviceShape).toPascalCase()) return symbolBuilder(shape, rustType).locatedIn(Models).build() @@ -76,7 +75,7 @@ class ConstrainedShapeSymbolProvider( is MapShape -> { if (shape.isDirectlyConstrained(base)) { check(shape.hasTrait()) { "Only the `length` constraint trait can be applied to maps" } - publicConstrainedSymbolForCollectionOrMapShape(shape) + publicConstrainedSymbolForMapShape(shape) } else { val keySymbol = this.toSymbol(shape.key) val valueSymbol = this.toSymbol(shape.value) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 7cc300b142..f4fcf55daf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -26,7 +26,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.toSnakeCase -// TODO Unit tests. /** * The [ConstraintViolationSymbolProvider] returns, for a given constrained * shape, a symbol whose Rust type can hold information about constraint diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index ae60275ccc..17d8ef748a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -54,11 +54,16 @@ fun serverTestCodegenContext( codegenTarget ) -fun serverTestSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSymbolProvider = +fun serverTestSymbolProvider( + model: Model, + serviceShape: ServiceShape? = null, + publicConstrainedTypesEnabled: Boolean = true +): RustSymbolProvider = RustCodegenServerPlugin.baseSymbolProvider( model, serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), - ServerTestSymbolVisitorConfig + ServerTestSymbolVisitorConfig, + publicConstrainedTypesEnabled = publicConstrainedTypesEnabled ) /** diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt new file mode 100644 index 0000000000..b3612f438f --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.util.lookup + +const val baseModelString = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + constrainedString: ConstrainedString, + constrainedMap: ConstrainedMap, + unconstrainedMap: TransitivelyConstrainedMap + } + + @length(min: 1, max: 69) + string ConstrainedString + + string UnconstrainedString + + @length(min: 1, max: 69) + map ConstrainedMap { + key: String, + value: String + } + + map TransitivelyConstrainedMap { + key: String, + value: ConstrainedMap + } + + @length(min: 1, max: 69) + list ConstrainedCollection { + member: String + } + """ + +class ConstrainedShapeSymbolProviderTest { + private val model = baseModelString.asSmithyModel() + private val serviceShape = model.lookup("test#TestService") + private val symbolProvider = serverTestSymbolProvider(model, serviceShape) + private val constrainedShapeSymbolProvider = ConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + + private val constrainedMapShape = model.lookup("test#ConstrainedMap") + private val constrainedMapType = constrainedShapeSymbolProvider.toSymbol(constrainedMapShape).rustType() + + @Test + fun `it should return a constrained string type for a constrained string shape`() { + val constrainedStringShape = model.lookup("test#ConstrainedString") + val constrainedStringType = constrainedShapeSymbolProvider.toSymbol(constrainedStringShape).rustType() + + constrainedStringType shouldBe RustType.Opaque("ConstrainedString", "crate::model") + } + + @Test + fun `it should return a constrained map type for a constrained map shape`() { + constrainedMapType shouldBe RustType.Opaque("ConstrainedMap", "crate::model") + } + + @Test + fun `it should not blindly delegate to the base symbol provider when the shape is an aggregate shape and is not directly constrained`() { + val unconstrainedMapShape = model.lookup("test#TransitivelyConstrainedMap") + val unconstrainedMapType = constrainedShapeSymbolProvider.toSymbol(unconstrainedMapShape).rustType() + + unconstrainedMapType shouldBe RustType.HashMap(RustType.String, constrainedMapType) + } + + @Test + fun `it should delegate to the base symbol provider for unconstrained simple shapes`() { + val unconstrainedStringShape = model.lookup("test#UnconstrainedString") + val unconstrainedStringSymbol = constrainedShapeSymbolProvider.toSymbol(unconstrainedStringShape) + + unconstrainedStringSymbol shouldBe symbolProvider.toSymbol(unconstrainedStringShape) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt new file mode 100644 index 0000000000..af7a010ec1 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -0,0 +1,115 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +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.ServiceShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.util.lookup + +class PubCrateConstrainedShapeSymbolProviderTest { + private val model = """ + $baseModelString + + list TransitivelyConstrainedCollection { + member: Structure + } + + structure Structure { + @required + requiredMember: String + } + + structure StructureWithMemberTargetingAggregateShape { + member: TransitivelyConstrainedCollection + } + + union Union { + structure: Structure + } + """.asSmithyModel() + + private val serviceShape = model.lookup("test#TestService") + private val symbolProvider = serverTestSymbolProvider(model, serviceShape, publicConstrainedTypesEnabled = false) + private val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + + @Test + fun `it should crash when provided with a shape that is directly constrained`() { + val constrainedStringShape = model.lookup("test#ConstrainedString") + shouldThrow { pubCrateConstrainedShapeSymbolProvider.toSymbol(constrainedStringShape) } + } + + @Test + fun `it should crash when provided with a shape that is unconstrained`() { + val unconstrainedStringShape = model.lookup("test#UnconstrainedString") + shouldThrow { pubCrateConstrainedShapeSymbolProvider.toSymbol(unconstrainedStringShape) } + } + + @Test + fun `it should return an opaque type for transitively constrained collection shapes`() { + val transitivelyConstrainedCollectionShape = model.lookup("test#TransitivelyConstrainedCollection") + val transitivelyConstrainedCollectionType = + pubCrateConstrainedShapeSymbolProvider.toSymbol(transitivelyConstrainedCollectionShape).rustType() + + transitivelyConstrainedCollectionType shouldBe RustType.Opaque( + "TransitivelyConstrainedCollectionConstrained", + "crate::constrained::transitively_constrained_collection_constrained" + ) + } + + @Test + fun `it should return an opaque type for transitively constrained map shapes`() { + val transitivelyConstrainedMapShape = model.lookup("test#TransitivelyConstrainedMap") + val transitivelyConstrainedMapType = + pubCrateConstrainedShapeSymbolProvider.toSymbol(transitivelyConstrainedMapShape).rustType() + + transitivelyConstrainedMapType shouldBe RustType.Opaque( + "TransitivelyConstrainedMapConstrained", + "crate::constrained::transitively_constrained_map_constrained" + ) + } + + @Test + fun `it should not blindly delegate to the base symbol provider when provided with a transitively constrained structure member shape targeting an aggregate shape`() { + val memberShape = model.lookup("test#StructureWithMemberTargetingAggregateShape\$member") + val memberType = pubCrateConstrainedShapeSymbolProvider.toSymbol(memberShape).rustType() + + memberType shouldBe RustType.Option( + RustType.Opaque( + "TransitivelyConstrainedCollectionConstrained", + "crate::constrained::transitively_constrained_collection_constrained" + ) + ) + } + + @Test + fun `it should delegate to the base symbol provider when provided with a structure shape`() { + val structureShape = model.lookup("test#TestInputOutput") + val structureSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(structureShape) + + structureSymbol shouldBe symbolProvider.toSymbol(structureShape) + } + + @Test + fun `it should delegate to the base symbol provider when provided with a union shape`() { + val unionShape = model.lookup("test#Union") + val unionSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(unionShape) + + unionSymbol shouldBe symbolProvider.toSymbol(unionShape) + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index 585f253c5a..871b39c86a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -31,12 +31,12 @@ class UnconstrainedShapeSymbolProviderTest { version: "123", operations: [TestOperation] } - + operation TestOperation { input: TestInputOutput, output: TestInputOutput, } - + structure TestInputOutput { list: ListA } @@ -47,15 +47,15 @@ class UnconstrainedShapeSymbolProviderTest { val model = """ $baseModelString - + list ListA { member: ListB } - + list ListB { member: StructureC } - + structure StructureC { @required string: String @@ -84,11 +84,11 @@ class UnconstrainedShapeSymbolProviderTest { val model = """ $baseModelString - + list ListA { member: StructureB } - + structure StructureB { string: String } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 23391e33f5..93928ab03c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.rust.codegen.util.PANIC import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase -// TODO Unit tests. /** * The [PubCrateConstrainedShapeSymbolProvider] returns, for a given * _transitively but not directly_ constrained shape, a symbol whose Rust type From 52be4456bad1a7eb02465448ce789251cbc986f4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 19:47:13 +0200 Subject: [PATCH 121/255] Rename to PubCrateConstrainedCollectionGenerator --- .../smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt | 4 ++-- ...Generator.kt => PubCrateConstrainedCollectionGenerator.kt} | 2 +- .../smithy/generators/UnconstrainedCollectionGeneratorTest.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/{PubCrateConstrainedCollectionShapeGenerator.kt => PubCrateConstrainedCollectionGenerator.kt} (99%) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 6bab9474d3..303eaae971 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -24,7 +24,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedTraitForEnumGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionShapeGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator @@ -266,7 +266,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: logger.info("[rust-server-codegen] Generating a constrained type for collection shape $shape") rustCrate.withModule(constrainedModule) { writer -> - PubCrateConstrainedCollectionShapeGenerator( + PubCrateConstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt similarity index 99% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 854532dc11..26fbece6b2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionShapeGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -36,7 +36,7 @@ import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained * shape is _directly_ constrained, use [ConstrainedCollectionGenerator] * instead. */ -class PubCrateConstrainedCollectionShapeGenerator( +class PubCrateConstrainedCollectionGenerator( val model: Model, val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 037b3366da..ef0cbd8436 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -75,7 +75,7 @@ class UnconstrainedCollectionGeneratorTest { val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { - PubCrateConstrainedCollectionShapeGenerator( + PubCrateConstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, From 0e4ce40b634db56c24503562bbcaea8eb42e0cf2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 19:49:50 +0200 Subject: [PATCH 122/255] Not doing unit tests for these --- .../server/smithy/generators/ConstrainedTraitForEnumGenerator.kt | 1 - .../smithy/generators/PubCrateConstrainedCollectionGenerator.kt | 1 - .../server/smithy/generators/PubCrateConstrainedMapGenerator.kt | 1 - 3 files changed, 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 34ef78fa73..ebbaea1427 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait -// TODO Unit tests /** * [ConstrainedTraitForEnumGenerator] TODO Docs */ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 26fbece6b2..b3205449a9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -19,7 +19,6 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained -// TODO Unit tests /** * A generator for a wrapper tuple newtype over a collection shape's symbol * type. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 3332423644..564aa1153f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -21,7 +21,6 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained -// TODO Unit tests /** * A generator for a wrapper tuple newtype over a map shape's symbol type. * From dc37a8dd85401a33a52db2c7f709e44d87c155cd Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 6 Jun 2022 20:04:23 +0200 Subject: [PATCH 123/255] Remove useless statement from InlineDependencyTest --- .../amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt index 8f6289d087..eb9736ebe3 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/rustlang/InlineDependencyTest.kt @@ -29,7 +29,6 @@ internal class InlineDependencyTest { fun `locate dependencies from the inlineable module`() { val dep = InlineDependency.idempotencyToken() val testWriter = RustWriter.root() - testWriter.compileAndTest() testWriter.addDependency(CargoDependency.FastRand) testWriter.withModule(dep.module.name) { dep.renderer(this) From ad86d7d3c0b257850f07d9517c7ecb0fba179d63 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 7 Jun 2022 14:46:17 +0200 Subject: [PATCH 124/255] Fail at trying to rewrite unit tests spanning multiple modules using project and unitTest() to writer.compileAndTest() --- .../UnconstrainedCollectionGeneratorTest.kt | 242 +++++++++--------- 1 file changed, 127 insertions(+), 115 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index ef0cbd8436..028fabffb1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -9,148 +9,160 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.testutil.unitTest import software.amazon.smithy.rust.codegen.util.lookup class UnconstrainedCollectionGeneratorTest { - @Test - fun `it should generate unconstrained lists`() { - val model = - """ - namespace test + private val model = + """ + namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - list: ListA - } - - list ListA { - member: ListB - } - - list ListB { - member: StructureC - } + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + list: ListA + } + + list ListA { + member: ListB + } + + list ListB { + member: StructureC + } + + structure StructureC { + @required + int: Integer, - structure StructureC { - @required - int: Integer, - - @required - string: String - } - """.asSmithyModel() - val symbolProvider = serverTestSymbolProvider(model) + @required + string: String + } + """.asSmithyModel() + private val symbolProvider = serverTestSymbolProvider(model) - val serviceShape = model.lookup("test#TestService") - val listA = model.lookup("test#ListA") - val listB = model.lookup("test#ListB") + private val serviceShape = model.lookup("test#TestService") + private val listA = model.lookup("test#ListA") + private val listB = model.lookup("test#ListB") - val project = TestWorkspace.testProject(symbolProvider) + private val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + private val pubCrateConstrainedShapeSymbolProvider = + PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - project.withModule(RustModule.public("model")) { writer -> - model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) + private fun renderModel(): RustWriter { + val writer = RustWriter.root() + val modelsModuleWriter = writer.withModule(ModelsModule.name) { + model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, this) } - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - project.withModule(RustModule.private("constrained")) { writer -> - listOf(listA, listB).forEach { + writer.withModule("constrained") { + listOf(listA, listB).forEach { shape -> PubCrateConstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, - writer, - it + this, + shape ).render() } } - project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> - project.withModule(ModelsModule) { modelsModuleWriter -> - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - it - ).render() - } - - unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_fail_to_constrain_with_first_error", - test = """ - let c_builder1 = crate::model::StructureC::builder().int(69); - let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let expected_err = - crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( - crate::model::structure_c::ConstraintViolation::MissingString, - )); - - assert_eq!( - expected_err, - crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() - ); - """ - ) - - unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_succeed_to_constrain", - test = """ - let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let expected: Vec> = vec![vec![crate::model::StructureC { - string: String::from("david"), - int: 69 - }]]; - let actual: Vec> = - crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); - - assert_eq!(expected, actual); - """ - ) - - unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_converts_into_constrained", - test = """ - let c_builder = crate::model::StructureC::builder(); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """ - ) - project.compileAndTest() + writer.withModule("unconstrained") { + val unconstrainedModuleWriter = this + + val constraintViolationSymbolProvider = + ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(listA, listB).forEach { + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + it + ).render() } } + + return writer + } + + @Test + fun `it should fail to constrain with the first error`() { + val writer = renderModel() + + writer.compileAndTest( + """ + let c_builder1 = crate::model::StructureC::builder().int(69); + let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected_err = + crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( + crate::model::structure_c::ConstraintViolation::MissingString, + )); + + assert_eq!( + expected_err, + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() + ); + """ + ) + } + + @Test + fun `it should succeed to constrain`() { + val writer = renderModel() + + writer.compileAndTest( + """ + let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected: Vec> = vec![vec![crate::model::StructureC { + string: String::from("david"), + int: 69 + }]]; + let actual: Vec> = + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); + + assert_eq!(expected, actual); + """ + ) + } + + @Test + fun `it should convert into MaybeConstrained`() { + val writer = renderModel() + + writer.compileAndTest( + """ + let c_builder = crate::model::StructureC::builder(); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); + """ + ) } } From eaf918b8fff12f08e4c2ac7d9232493facb84ab6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 7 Jun 2022 14:46:54 +0200 Subject: [PATCH 125/255] Revert "Fail at trying to rewrite unit tests spanning multiple modules using project and unitTest() to writer.compileAndTest()" This reverts commit ad86d7d3c0b257850f07d9517c7ecb0fba179d63. --- .../UnconstrainedCollectionGeneratorTest.kt | 242 +++++++++--------- 1 file changed, 115 insertions(+), 127 deletions(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 028fabffb1..ef0cbd8436 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -9,160 +9,148 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest import software.amazon.smithy.rust.codegen.util.lookup class UnconstrainedCollectionGeneratorTest { - private val model = - """ - namespace test + @Test + fun `it should generate unconstrained lists`() { + val model = + """ + namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - list: ListA - } - - list ListA { - member: ListB - } - - list ListB { - member: StructureC - } - - structure StructureC { - @required - int: Integer, + service TestService { + version: "123", + operations: [TestOperation] + } - @required - string: String - } - """.asSmithyModel() - private val symbolProvider = serverTestSymbolProvider(model) + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + list: ListA + } + + list ListA { + member: ListB + } + + list ListB { + member: StructureC + } + + structure StructureC { + @required + int: Integer, + + @required + string: String + } + """.asSmithyModel() + val symbolProvider = serverTestSymbolProvider(model) - private val serviceShape = model.lookup("test#TestService") - private val listA = model.lookup("test#ListA") - private val listB = model.lookup("test#ListB") + val serviceShape = model.lookup("test#TestService") + val listA = model.lookup("test#ListA") + val listB = model.lookup("test#ListB") - private val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - private val pubCrateConstrainedShapeSymbolProvider = - PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val project = TestWorkspace.testProject(symbolProvider) - private fun renderModel(): RustWriter { - val writer = RustWriter.root() - val modelsModuleWriter = writer.withModule(ModelsModule.name) { - model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, this) + project.withModule(RustModule.public("model")) { writer -> + model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } - writer.withModule("constrained") { - listOf(listA, listB).forEach { shape -> + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + project.withModule(RustModule.private("constrained")) { writer -> + listOf(listA, listB).forEach { PubCrateConstrainedCollectionGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, - this, - shape + writer, + it ).render() } } - writer.withModule("unconstrained") { - val unconstrainedModuleWriter = this - - val constraintViolationSymbolProvider = - ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - it - ).render() + project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> + project.withModule(ModelsModule) { modelsModuleWriter -> + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + listOf(listA, listB).forEach { + UnconstrainedCollectionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + it + ).render() + } + + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_fail_to_constrain_with_first_error", + test = """ + let c_builder1 = crate::model::StructureC::builder().int(69); + let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected_err = + crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( + crate::model::structure_c::ConstraintViolation::MissingString, + )); + + assert_eq!( + expected_err, + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() + ); + """ + ) + + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_succeed_to_constrain", + test = """ + let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let expected: Vec> = vec![vec![crate::model::StructureC { + string: String::from("david"), + int: 69 + }]]; + let actual: Vec> = + crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); + + assert_eq!(expected, actual); + """ + ) + + unconstrainedModuleWriter.unitTest( + name = "list_a_unconstrained_converts_into_constrained", + test = """ + let c_builder = crate::model::StructureC::builder(); + let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + + let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); + """ + ) + project.compileAndTest() } } - - return writer - } - - @Test - fun `it should fail to constrain with the first error`() { - val writer = renderModel() - - writer.compileAndTest( - """ - let c_builder1 = crate::model::StructureC::builder().int(69); - let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let expected_err = - crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( - crate::model::structure_c::ConstraintViolation::MissingString, - )); - - assert_eq!( - expected_err, - crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() - ); - """ - ) - } - - @Test - fun `it should succeed to constrain`() { - val writer = renderModel() - - writer.compileAndTest( - """ - let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let expected: Vec> = vec![vec![crate::model::StructureC { - string: String::from("david"), - int: 69 - }]]; - let actual: Vec> = - crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); - - assert_eq!(expected, actual); - """ - ) - } - - @Test - fun `it should convert into MaybeConstrained`() { - val writer = renderModel() - - writer.compileAndTest( - """ - let c_builder = crate::model::StructureC::builder(); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - - let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """ - ) } } From a2f99806a47bc6f318f03cd764e5e7a717c84c94 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 7 Jun 2022 16:21:43 +0200 Subject: [PATCH 126/255] ConstrainedStringGeneratorTest --- .../generators/ConstrainedStringGenerator.kt | 1 - .../ConstrainedStringGeneratorTest.kt | 183 ++++++++++++++++++ .../UnconstrainedCollectionGeneratorTest.kt | 6 +- .../UnconstrainedMapGeneratorTest.kt | 6 +- 4 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index e9e9c3e7b7..8dc179c50c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -20,7 +20,6 @@ import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.toSnakeCase -// TODO Unit tests /** * [ConstrainedStringGenerator] generates a wrapper tuple newtype holding a constrained `String`. * This type can be built from unconstrained values, yielding a `ConstraintViolation` when the input does not satisfy diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt new file mode 100644 index 0000000000..1f825d1cb2 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -0,0 +1,183 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest +import software.amazon.smithy.rust.codegen.util.lookup +import java.util.stream.Stream + +private const val baseModelString = """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + constrainedString: ConstrainedString + } + """ + +class ConstrainedStringGeneratorTest { + + data class TestCase(val model: Model, val validString: String, val invalidString: String) + + class ConstrainedStringGeneratorTestProvider : ArgumentsProvider { + private val testCases = listOf( + // Min and max. + Triple("@length(min: 11, max: 12)", "validString", "invalidString"), + // Min equal to max. + Triple("@length(min: 11, max: 11)", "validString", "invalidString"), + // Only min. + Triple("@length(min: 11)", "validString", ""), + // Only max. + Triple("@length(max: 11)", "", "invalidString"), + // Count Unicode scalar values, not `.len()`. + Triple( + "@length(min: 3, max: 3)", + "👍👍👍", // These three emojis are three Unicode scalar values. + "👍👍👍👍", + ) + ).map { + TestCase( + """ + $baseModelString + + ${it.first} + string ConstrainedString + """.asSmithyModel(), + it.second, + it.third + ) + } + + override fun provideArguments(context: ExtensionContext?): Stream = + testCases.map { Arguments.of(it) }.stream() + } + + @ParameterizedTest + @ArgumentsSource(ConstrainedStringGeneratorTestProvider::class) + fun `it should generate constrained string types`(testCase: TestCase) { + val serviceShape = testCase.model.lookup("test#TestService") + val constrainedStringShape = testCase.model.lookup("test#ConstrainedString") + + val symbolProvider = serverTestSymbolProvider(testCase.model) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, testCase.model, serviceShape) + + val project = TestWorkspace.testProject(symbolProvider) + + project.withModule(ModelsModule) { writer -> + ConstrainedStringGenerator( + testCase.model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedStringShape + ).render() + + writer.unitTest( + name = "parse_success", + test = """ + let string = String::from("${testCase.validString}"); + let _constrained = ConstrainedString::parse(string).unwrap(); + """ + ) + writer.unitTest( + name = "try_from_success", + test = """ + let string = String::from("${testCase.validString}"); + let _constrained: ConstrainedString = string.try_into().unwrap(); + """ + ) + writer.unitTest( + name = "parse_fail", + test = """ + let string = String::from("${testCase.invalidString}"); + let _constrained = ConstrainedString::parse(string).unwrap_err(); + """ + ) + writer.unitTest( + name = "try_from_fail", + test = """ + let string = String::from("${testCase.invalidString}"); + let constrained_res: Result = string.try_into(); + constrained_res.unwrap_err(); + """ + ) + writer.unitTest( + name = "inner", + test = """ + let string = String::from("${testCase.validString}"); + let constrained = ConstrainedString::parse(string).unwrap(); + + assert_eq!(constrained.inner(), "${testCase.validString}"); + """ + ) + writer.unitTest( + name = "into_inner", + test = """ + let string = String::from("${testCase.validString}"); + let constrained = ConstrainedString::parse(string.clone()).unwrap(); + + assert_eq!(constrained.into_inner(), string); + """ + ) + } + + project.compileAndTest() + } + + @Test + fun `type should not be constructible without using a constructor`() { + val model = """ + $baseModelString + + @length(min: 1, max: 69) + string ConstrainedString + """.asSmithyModel() + val serviceShape = model.lookup("test#TestService") + val constrainedStringShape = model.lookup("test#ConstrainedString") + + val symbolProvider = serverTestSymbolProvider(model) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + + val writer = RustWriter.forModule(ModelsModule.name) + + ConstrainedStringGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedStringShape + ).render() + + // Check that the wrapped type is `pub(crate)`. + writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index ef0cbd8436..72fc6010ae 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -118,7 +118,7 @@ class UnconstrainedCollectionGeneratorTest { expected_err, crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); - """ + """ ) unconstrainedModuleWriter.unitTest( @@ -136,7 +136,7 @@ class UnconstrainedCollectionGeneratorTest { crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); assert_eq!(expected, actual); - """ + """ ) unconstrainedModuleWriter.unitTest( @@ -147,7 +147,7 @@ class UnconstrainedCollectionGeneratorTest { let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """ + """ ) project.compileAndTest() } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 3298543261..f8983822cb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -134,7 +134,7 @@ class UnconstrainedMapGeneratorTest { let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); assert!(actual_err == missing_string_expected_err || actual_err == missing_int_expected_err); - """ + """ ) unconstrainedModuleWriter.unitTest( @@ -165,7 +165,7 @@ class UnconstrainedMapGeneratorTest { expected, crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); - """ + """ ) unconstrainedModuleWriter.unitTest( @@ -184,7 +184,7 @@ class UnconstrainedMapGeneratorTest { ); let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); - """ + """ ) project.compileAndTest() From f7218d2980a4f8ef9934bcdfb1d446514ccac59f Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 8 Jun 2022 19:19:53 +0200 Subject: [PATCH 127/255] ConstrainedMapGeneratorTest --- .../server/smithy/ServerCodegenVisitor.kt | 64 +++-- .../generators/ConstrainedMapGenerator.kt | 28 ++- .../ConstrainedMapGeneratorCommon.kt | 22 ++ .../ConstrainedShapeGeneratorCommon.kt | 2 +- .../generators/ConstrainedStringGenerator.kt | 1 + .../MapConstraintViolationGenerator.kt | 60 +++++ .../generators/UnconstrainedMapGenerator.kt | 52 +--- .../generators/ConstrainedMapGeneratorTest.kt | 226 ++++++++++++++++++ .../UnconstrainedMapGeneratorTest.kt | 7 + 9 files changed, 378 insertions(+), 84 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 303eaae971..7dda7b76a5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -24,6 +24,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedTraitForEnumGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.MapConstraintViolationGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator @@ -279,39 +280,24 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } override fun mapShape(shape: MapShape) { - if (shape.isDirectlyConstrained(symbolProvider)) { - rustCrate.useShapeWriter(shape) { writer -> - ConstrainedMapGenerator( + val renderUnconstrainedMap = + shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + model, + symbolProvider + ) + if (renderUnconstrainedMap) { + logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") + rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> + UnconstrainedMapGenerator( model, symbolProvider, unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, - writer, + unconstrainedModuleWriter, shape ).render() } - } - - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( - model, - symbolProvider - ) - ) { - logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") - rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> - rustCrate.withModule(ModelsModule) { modelsModuleWriter -> - UnconstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - shape - ).render() - } - } if (!shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") @@ -327,6 +313,32 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: } } } + + val isDirectlyConstrained = shape.isDirectlyConstrained(symbolProvider) + if (isDirectlyConstrained) { + rustCrate.useShapeWriter(shape) { writer -> + ConstrainedMapGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null + ).render() + } + } + + if (isDirectlyConstrained || renderUnconstrainedMap) { + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> + MapConstraintViolationGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + modelsModuleWriter, + shape + ).render() + } + } } /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 3f636b4ed4..18b70a7fb8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.traits.LengthTrait @@ -14,7 +15,6 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.util.expectTrait // TODO Unit tests @@ -25,15 +25,18 @@ import software.amazon.smithy.rust.codegen.util.expectTrait * * The [`length` trait] is the only constraint trait applicable to map shapes. * + * If [unconstrainedSymbol] is provided, the `MaybeConstrained` trait is implemented for the constrained type, using the + * [unconstrainedSymbol]'s associated type as the associated type for the trait. + * * [`length` trait]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait */ class ConstrainedMapGenerator( val model: Model, val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, val writer: RustWriter, - val shape: MapShape + val shape: MapShape, + private val unconstrainedSymbol: Symbol? = null, ) { fun render() { // The `length` trait is the only constraint trait applicable to map shapes. @@ -41,7 +44,6 @@ class ConstrainedMapGenerator( val name = symbolProvider.toSymbol(shape).name val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" - // TODO This won't work if the map is only used in operation output, because we don't render the constraint violation symbol. val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { @@ -75,10 +77,6 @@ class ConstrainedMapGenerator( } } - impl #{ConstrainedTrait} for $name { - type Unconstrained = #{UnconstrainedSymbol}; - } - impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; @@ -94,10 +92,20 @@ class ConstrainedMapGenerator( """, "KeySymbol" to symbolProvider.toSymbol(model.expectShape(shape.key.target)), "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "UnconstrainedSymbol" to unconstrainedShapeSymbolProvider.toSymbol(shape), "TryFrom" to RuntimeType.TryFrom, "ConstraintViolation" to constraintViolation ) + + if (unconstrainedSymbol != null) { + writer.rustTemplate( + """ + impl #{ConstrainedTrait} for $name { + type Unconstrained = #{UnconstrainedSymbol}; + } + """, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt new file mode 100644 index 0000000000..9d0fa0d7b2 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained + +/** + * Common helper functions used in [UnconstrainedMapGenerator] and [MapConstraintViolationGenerator]. + */ + +fun isKeyConstrained(shape: StringShape, symbolProvider: SymbolProvider) = shape.isDirectlyConstrained(symbolProvider) + +fun isValueConstrained(shape: Shape, model: Model, symbolProvider: SymbolProvider): Boolean = + shape.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt index cbeade5013..bf67e043df 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators /** - * Functions shared amongst the constrained shape generators, to keep the DRY and consistent. + * Functions shared amongst the constrained shape generators, to keep them DRY and consistent. */ fun rustDocsNote(typeName: String) = diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 8dc179c50c..cc56368955 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -38,6 +38,7 @@ class ConstrainedStringGenerator( val symbol = symbolProvider.toSymbol(shape) val name = symbol.name val inner = RustType.String.render() + // TODO This won't work if the map is only used in operation output, because we don't render the constraint violation symbol. val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt new file mode 100644 index 0000000000..7cc64685fd --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.util.hasTrait + +class MapConstraintViolationGenerator( + val model: Model, + val symbolProvider: RustSymbolProvider, + private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val modelsModuleWriter: RustWriter, + val shape: MapShape +) { + fun render() { + val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + val valueShape = model.expectShape(shape.value.target) + val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) + val constraintViolationName = constraintViolationSymbol.name + + val constraintViolationCodegenScope = listOfNotNull( + if (isKeyConstrained(keyShape, symbolProvider)) { + "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) + } else { + null + }, + if (isValueConstrained(valueShape, model, symbolProvider)) { + "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape) + } else { + null + }, + ).toTypedArray() + + modelsModuleWriter.withModule( + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + ) { + rustTemplate( + """ + ##[derive(Debug, PartialEq)] + pub enum $constraintViolationName { + ${if (shape.hasTrait()) "Length(usize)," else ""} + ${if (isKeyConstrained(keyShape, symbolProvider)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} + ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{ValueConstraintViolationSymbol})," else ""} + } + """, + *constraintViolationCodegenScope, + ) + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index a70a02dfaa..210f102c5b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -7,9 +7,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility @@ -23,7 +21,6 @@ import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvid import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.util.hasTrait // TODO Docs class UnconstrainedMapGenerator( @@ -31,15 +28,13 @@ class UnconstrainedMapGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val unconstrainedModuleWriter: RustWriter, - private val modelsModuleWriter: RustWriter, val shape: MapShape ) { private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) private val name = symbol.name private val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) - private val constraintViolationName = constraintViolationSymbol.name private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) private val valueShape = model.expectShape(shape.value.target) private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { @@ -75,39 +70,6 @@ class UnconstrainedMapGenerator( renderTryFromUnconstrainedForConstrained(this) } - - renderConstraintViolation() - } - - private fun renderConstraintViolation() { - val constraintViolationCodegenScope = listOfNotNull( - if (isKeyConstrained(keyShape)) { - "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) - } else { - null - }, - if (isValueConstrained(valueShape)) { - "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape) - } else { - null - }, - ).toTypedArray() - - modelsModuleWriter.withModule( - constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() - ) { - rustTemplate( - """ - ##[derive(Debug, PartialEq)] - pub enum $constraintViolationName { - ${if (shape.hasTrait()) "Length(usize)," else ""} - ${if (isKeyConstrained(keyShape)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} - ${if (isValueConstrained(valueShape)) "##[doc(hidden)] Value(#{ValueConstraintViolationSymbol})," else ""} - } - """, - *constraintViolationCodegenScope, - ) - } } private fun renderTryFromUnconstrainedForConstrained(writer: RustWriter) { @@ -115,9 +77,9 @@ class UnconstrainedMapGenerator( rust("type Error = #T;", constraintViolationSymbol) rustBlock("fn try_from(value: $name) -> Result") { - if (isKeyConstrained(keyShape) || isValueConstrained(valueShape)) { + if (isKeyConstrained(keyShape, symbolProvider) || isValueConstrained(valueShape, model, symbolProvider)) { val resolveToNonPublicConstrainedValueType = - isValueConstrained(valueShape) && + isValueConstrained(valueShape, model, symbolProvider) && !valueShape.isDirectlyConstrained(symbolProvider) && !valueShape.isStructureShape val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { @@ -131,8 +93,8 @@ class UnconstrainedMapGenerator( let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { - ${if (isKeyConstrained(keyShape)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} - ${if (isValueConstrained(valueShape)) "let v = v.try_into().map_err(Self::Error::Value)?;" else ""} + ${if (isKeyConstrained(keyShape, symbolProvider)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} + ${if (isValueConstrained(valueShape, model, symbolProvider)) "let v = v.try_into().map_err(Self::Error::Value)?;" else ""} Ok((k, v)) }) .collect(); @@ -190,8 +152,4 @@ class UnconstrainedMapGenerator( } } } - - private fun isKeyConstrained(shape: StringShape) = shape.isDirectlyConstrained(symbolProvider) - - private fun isValueConstrained(shape: Shape): Boolean = shape.canReachConstrainedShape(model, symbolProvider) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt new file mode 100644 index 0000000000..1e5ccc8e56 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -0,0 +1,226 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.Instantiator +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest +import software.amazon.smithy.rust.codegen.util.lookup +import java.util.stream.Stream + +private const val baseModelString = """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + constrainedMap: ConstrainedMap + } + """ + +class ConstrainedMapGeneratorTest { + + data class TestCase(val model: Model, val validMap: ObjectNode, val invalidMap: ObjectNode) + + class ConstrainedMapGeneratorTestProvider : ArgumentsProvider { + private val testCases = listOf( + // Min and max. + Triple("@length(min: 11, max: 12)", 11, 13), + // Min equal to max. + Triple("@length(min: 11, max: 11)", 11, 12), + // Only min. + Triple("@length(min: 11)", 15, 10), + // Only max. + Triple("@length(max: 11)", 11, 12), + ).map { + val validStringMap = List(it.second) { index -> index.toString() to "value" }.toMap() + val inValidStringMap = List(it.third) { index -> index.toString() to "value" }.toMap() + Triple(it.first, ObjectNode.fromStringMap(validStringMap), ObjectNode.fromStringMap(inValidStringMap)) + }.map { + TestCase( + """ + $baseModelString + + ${it.first} + map ConstrainedMap { + key: String, + value: String + } + """.asSmithyModel(), + it.second, + it.third + ) + } + + override fun provideArguments(context: ExtensionContext?): Stream = + testCases.map { Arguments.of(it) }.stream() + } + + @ParameterizedTest + @ArgumentsSource(ConstrainedMapGeneratorTestProvider::class) + fun `it should generate constrained map types`(testCase: TestCase) { + val serviceShape = testCase.model.lookup("test#TestService") + val constrainedMapShape = testCase.model.lookup("test#ConstrainedMap") + + val codegenContext = serverTestCodegenContext(testCase.model, serviceShape) + val symbolProvider = codegenContext.symbolProvider + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, testCase.model, serviceShape) + + val project = TestWorkspace.testProject(symbolProvider) + + project.withModule(ModelsModule) { writer -> + render( + testCase.model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedMapShape + ) + + val instantiator = + Instantiator(symbolProvider, testCase.model, codegenContext.runtimeConfig, codegenContext.target) + writer.rustBlock("##[cfg(test)] fn build_valid_map() -> std::collections::HashMap") { + instantiator.render(this, constrainedMapShape, testCase.validMap) + } + writer.rustBlock("##[cfg(test)] fn build_invalid_map() -> std::collections::HashMap") { + instantiator.render(this, constrainedMapShape, testCase.invalidMap) + } + + writer.unitTest( + name = "parse_success", + test = """ + let map = build_valid_map(); + let _constrained = ConstrainedMap::parse(map).unwrap(); + """ + ) + writer.unitTest( + name = "try_from_success", + test = """ + let map = build_valid_map(); + let _constrained: ConstrainedMap = map.try_into().unwrap(); + """ + ) + writer.unitTest( + name = "parse_fail", + test = """ + let map = build_invalid_map(); + let _constrained = ConstrainedMap::parse(map).unwrap_err(); + """ + ) + writer.unitTest( + name = "try_from_fail", + test = """ + let map = build_invalid_map(); + let constrained_res: Result = map.try_into(); + constrained_res.unwrap_err(); + """ + ) + writer.unitTest( + name = "inner", + test = """ + let map = build_valid_map(); + let constrained = ConstrainedMap::parse(map.clone()).unwrap(); + + assert_eq!(constrained.inner(), &map); + """ + ) + writer.unitTest( + name = "into_inner", + test = """ + let map = build_valid_map(); + let constrained = ConstrainedMap::parse(map.clone()).unwrap(); + + assert_eq!(constrained.into_inner(), map); + """ + ) + } + + project.compileAndTest() + } + + @Test + fun `type should not be constructible without using a constructor`() { + val model = """ + $baseModelString + + @length(min: 1, max: 69) + map ConstrainedMap { + key: String, + value: String + } + """.asSmithyModel() + val serviceShape = model.lookup("test#TestService") + val constrainedMapShape = model.lookup("test#ConstrainedMap") + + val symbolProvider = serverTestSymbolProvider(model) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + + val writer = RustWriter.forModule(ModelsModule.name) + + render( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedMapShape + ) + + // Check that the wrapped type is `pub(crate)`. + writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) std::collections::HashMap);" + } + + private fun render( + model: Model, + symbolProvider: RustSymbolProvider, + constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + writer: RustWriter, + constrainedMapShape: MapShape + ) { + ConstrainedMapGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedMapShape + ).render() + + MapConstraintViolationGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + constrainedMapShape + ).render() + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index f8983822cb..a88f844a83 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -99,6 +99,13 @@ class UnconstrainedMapGeneratorTest { pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, unconstrainedModuleWriter, + it + ).render() + + MapConstraintViolationGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, modelsModuleWriter, it ).render() From 6d78301b1cc35925c32ac6325d40ff981f6949ef Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 9 Jun 2022 11:30:34 +0200 Subject: [PATCH 128/255] Remove TODO addressed in previous commit --- .../codegen/server/smithy/generators/ConstrainedMapGenerator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 18b70a7fb8..12e95995d0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.util.expectTrait -// TODO Unit tests /** * [ConstrainedMapGenerator] generates a wrapper tuple newtype holding a constrained `std::collections::HashMap`. * This type can be built from unconstrained values, yielding a `ConstraintViolation` when the input does not satisfy From c56d9fb1540595497c4959ddb952da88509f7166 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 9 Jun 2022 13:34:14 +0200 Subject: [PATCH 129/255] Remove incorrect TODO --- .../server/smithy/generators/ConstrainedStringGenerator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index cc56368955..8dc179c50c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -38,7 +38,6 @@ class ConstrainedStringGenerator( val symbol = symbolProvider.toSymbol(shape) val name = symbol.name val inner = RustType.String.render() - // TODO This won't work if the map is only used in operation output, because we don't render the constraint violation symbol. val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val condition = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { From d327f31de8e06f8e3d83224da9c43e66f88d68d9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 9 Jun 2022 14:00:25 +0200 Subject: [PATCH 130/255] UnconstrainedUnionGeneratorTest --- .../generators/UnconstrainedUnionGenerator.kt | 1 - .../UnconstrainedUnionGeneratorTest.kt | 133 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index cf7c639378..1c7d21add5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -34,7 +34,6 @@ import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase // TODO Docs -// TODO UnitTests class UnconstrainedUnionGenerator( val model: Model, val symbolProvider: RustSymbolProvider, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt new file mode 100644 index 0000000000..fb11209272 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -0,0 +1,133 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.testutil.unitTest +import software.amazon.smithy.rust.codegen.util.lookup + +class UnconstrainedUnionGeneratorTest { + @Test + fun `it should generate unconstrained unions`() { + val model = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + + structure TestInputOutput { + union: Union + } + + union Union { + structure: Structure + } + + structure Structure { + @required + requiredMember: String + } + """.asSmithyModel() + val symbolProvider = serverTestSymbolProvider(model) + + val serviceShape = model.lookup("test#TestService") + val unionShape = model.lookup("test#Union") + + val project = TestWorkspace.testProject(symbolProvider) + + project.withModule(RustModule.public("model")) { writer -> + model.lookup("test#Structure").serverRenderWithModelBuilder(model, symbolProvider, writer) + } + + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + project.withModule(ModelsModule) { writer -> + UnionGenerator(model, symbolProvider, writer, unionShape, renderUnknownVariant = false).render() + } + project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> + project.withModule(ModelsModule) { modelsModuleWriter -> + val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + + UnconstrainedUnionGenerator( + model, + symbolProvider, + unconstrainedShapeSymbolProvider, + pubCrateConstrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + unconstrainedModuleWriter, + modelsModuleWriter, + unionShape + ).render() + + unconstrainedModuleWriter.unitTest( + name = "unconstrained_union_fail_to_constrain", + test = """ + let builder = crate::model::Structure::builder(); + let union_unconstrained = union_unconstrained::UnionUnconstrained::Structure(builder); + + let expected_err = crate::model::union::ConstraintViolation::StructureConstraintViolation( + crate::model::structure::ConstraintViolation::MissingRequiredMember, + ); + + assert_eq!( + expected_err, + crate::model::Union::try_from(union_unconstrained).unwrap_err() + ); + """ + ) + + unconstrainedModuleWriter.unitTest( + name = "unconstrained_union_succeed_to_constrain", + test = """ + let builder = crate::model::Structure::builder().required_member(String::from("david")); + let union_unconstrained = union_unconstrained::UnionUnconstrained::Structure(builder); + + let expected: crate::model::Union = crate::model::Union::Structure(crate::model::Structure { + required_member: String::from("david"), + }); + let actual: crate::model::Union = crate::model::Union::try_from(union_unconstrained).unwrap(); + + assert_eq!(expected, actual); + """ + ) + + unconstrainedModuleWriter.unitTest( + name = "unconstrained_union_converts_into_constrained", + test = """ + let builder = crate::model::Structure::builder(); + let union_unconstrained = union_unconstrained::UnionUnconstrained::Structure(builder); + + let _union: crate::constrained::MaybeConstrained = + union_unconstrained.into(); + """ + ) + project.compileAndTest() + } + } + } +} From 3bdb1ca1d88cd43503dbc7c650ba1e91a49a4087 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 10 Jun 2022 12:15:57 +0200 Subject: [PATCH 131/255] save work --- .../rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 5e3400f44e..df2403a9a2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -68,6 +68,7 @@ class JsonParserGenerator( ) : StructuredDataParserGenerator { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + // TODO Grab it private val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, codegenContext.serviceShape) private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target From 0b9cd6bf0ffbfbd009ebd807c0dab8e4d66b930f Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 14 Jun 2022 14:28:58 +0200 Subject: [PATCH 132/255] Comment out @error message constrained string type --- codegen-server-test/model/constraints.smithy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 60fcf48003..f0a900d5c9 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -360,8 +360,10 @@ map ConBMap { @error("client") structure ErrorWithLengthStringMessage { - @required - message: LengthString + // TODO Doesn't work yet because constrained string types don't implement + // `AsRef`. + // @required + // message: LengthString } map MapOfMapOfListOfListOfConB { From d51238d66fa180a4ad51cf9a59b598deba38cbb5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 15 Jun 2022 14:41:47 +0200 Subject: [PATCH 133/255] CodegenContext split: cst, sst, sdk work CodegenContext split: haven't tested sdk but all looks good --- .../smithy/rustsdk/AwsCodegenDecorator.kt | 5 +- .../smithy/rustsdk/AwsEndpointDecorator.kt | 5 +- .../rustsdk/AwsFluentClientDecorator.kt | 5 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 5 +- .../smithy/rustsdk/AwsReadmeDecorator.kt | 6 +- .../smithy/rustsdk/CrateLicenseDecorator.kt | 5 +- .../smithy/rustsdk/CredentialProviders.kt | 5 +- .../rustsdk/IntegrationTestDependencies.kt | 5 +- .../amazon/smithy/rustsdk/RegionDecorator.kt | 5 +- .../smithy/rustsdk/RetryPolicyDecorator.kt | 5 +- .../smithy/rustsdk/SdkConfigDecorator.kt | 5 +- .../smithy/rustsdk/ServiceConfigDecorator.kt | 5 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 5 +- .../smithy/rustsdk/UserAgentDecorator.kt | 5 +- .../apigateway/ApiGatewayDecorator.kt | 5 +- .../customize/auth/DisabledAuthDecorator.kt | 5 +- .../rustsdk/customize/ec2/Ec2Decorator.kt | 5 +- .../customize/glacier/GlacierDecorator.kt | 5 +- .../customize/route53/Route53Decorator.kt | 5 +- .../rustsdk/customize/s3/S3Decorator.kt | 19 +- .../python/smithy/RustCodegenServerPlugin.kt | 8 +- .../server/smithy/RustCodegenServerPlugin.kt | 20 +- .../server/smithy/ServerCodegenVisitor.kt | 23 +- .../server/smithy/ServerRustSettings.kt | 85 ------- .../AdditionalErrorsDecorator.kt | 19 +- .../ServerRequiredCustomizations.kt | 40 ++++ .../server/smithy/protocols/ServerAwsJson.kt | 8 +- .../ServerHttpBoundProtocolGenerator.kt | 12 +- .../smithy/protocols/ServerProtocolLoader.kt | 5 +- .../smithy/protocols/ServerRestJsonFactory.kt | 8 +- .../smithy/protocols/ServerRestXmlFactory.kt | 8 +- .../smithy/testutil/ServerTestHelpers.kt | 79 ++++--- .../rust/codegen/smithy/CodegenContext.kt | 70 +++--- .../rust/codegen/smithy/CodegenVisitor.kt | 17 +- .../rust/codegen/smithy/RustCodegenPlugin.kt | 20 +- .../rust/codegen/smithy/RustSettings.kt | 211 ++++++++++-------- .../codegen/smithy/ServerCodegenContext.kt | 24 ++ .../rust/codegen/smithy/ServerRustSettings.kt | 124 ++++++++++ .../rust/codegen/smithy/SymbolVisitor.kt | 18 +- .../customizations/AllowLintsGenerator.kt | 6 +- .../customizations/ClientCustomizations.kt | 14 +- .../customizations/ClientDocsGenerator.kt | 10 +- .../customizations/DocsRsMetadataDecorator.kt | 5 +- .../customizations/RetryConfigDecorator.kt | 7 +- .../customizations/SleepImplDecorator.kt | 5 +- .../customizations/TimeoutConfigDecorator.kt | 5 +- .../customize/RequiredCustomizations.kt | 30 +-- .../smithy/customize/RustCodegenDecorator.kt | 55 +++-- .../smithy/generators/ServiceGenerator.kt | 3 +- .../client/FluentClientDecorator.kt | 13 +- .../rust/codegen/smithy/protocols/AwsJson.kt | 10 +- .../rust/codegen/smithy/protocols/AwsQuery.kt | 7 +- .../rust/codegen/smithy/protocols/Ec2Query.kt | 7 +- .../rust/codegen/smithy/protocols/Protocol.kt | 13 +- .../rust/codegen/smithy/protocols/RestJson.kt | 7 +- .../rust/codegen/smithy/protocols/RestXml.kt | 11 +- .../protocols/parse/JsonParserGenerator.kt | 55 ++--- .../smithy/rust/codegen/testutil/Rust.kt | 22 +- .../rust/codegen/testutil/TestHelpers.kt | 24 +- .../HttpVersionListGeneratorTest.kt | 35 ++- .../smithy/EventStreamSymbolProviderTest.kt | 5 +- .../generators/EndpointTraitBindingsTest.kt | 14 +- .../protocol/ProtocolTestGeneratorTest.kt | 21 +- .../EventStreamUnmarshallerGeneratorTest.kt | 5 +- .../EventStreamMarshallerGeneratorTest.kt | 5 +- .../RemoveEventStreamOperationsTest.kt | 8 +- 66 files changed, 800 insertions(+), 516 deletions(-) delete mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index 9c06c6c2b3..4608d3fd90 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rustsdk +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.customizations.DocsRsMetadataDecorator import software.amazon.smithy.rust.codegen.smithy.customizations.DocsRsMetadataSettings import software.amazon.smithy.rust.codegen.smithy.customizations.RetryConfigDecorator @@ -51,7 +52,9 @@ val DECORATORS = listOf( DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)) ) -class AwsCodegenDecorator : CombinedCodegenDecorator(DECORATORS) { +class AwsCodegenDecorator : CombinedCodegenDecorator(DECORATORS) { override val name: String = "AwsSdkCodegenDecorator" override val order: Byte = -1 + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt index c33706ab67..de13dd25c3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -36,7 +37,7 @@ import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.orNull -class AwsEndpointDecorator : RustCodegenDecorator { +class AwsEndpointDecorator : RustCodegenDecorator { override val name: String = "AwsEndpoint" override val order: Byte = 0 @@ -66,6 +67,8 @@ class AwsEndpointDecorator : RustCodegenDecorator { ): List { return baseCustomizations + PubUseEndpoint(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class EndpointConfigCustomization(private val codegenContext: CodegenContext, private val endpointData: ObjectNode) : diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index 0487e8f3b0..0b367674f3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -72,7 +73,7 @@ private class AwsClientGenerics(private val types: Types) : FluentClientGenerics override fun sendBounds(input: Symbol, output: Symbol, error: RuntimeType): Writable = writable { } } -class AwsFluentClientDecorator : RustCodegenDecorator { +class AwsFluentClientDecorator : RustCodegenDecorator { override val name: String = "FluentClient" // Must run after the AwsPresigningDecorator so that the presignable trait is correctly added to operations @@ -110,6 +111,8 @@ class AwsFluentClientDecorator : RustCodegenDecorator { } } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } private class AwsFluentClientExtensions(types: Types) { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index bb1e01681e..90f48a4f85 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -27,6 +27,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization @@ -84,7 +85,7 @@ internal val PRESIGNABLE_OPERATIONS by lazy { class AwsPresigningDecorator internal constructor( private val presignableOperations: Map = PRESIGNABLE_OPERATIONS -) : RustCodegenDecorator { +) : RustCodegenDecorator { companion object { const val ORDER: Byte = 0 } @@ -126,6 +127,8 @@ class AwsPresigningDecorator internal constructor( } }.build() } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class AwsInputPresignedMethod( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt index 088fe5e6af..b6472b7c90 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsReadmeDecorator.kt @@ -10,12 +10,12 @@ import org.jsoup.nodes.Element import org.jsoup.nodes.TextNode import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.rust.codegen.rustlang.raw +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations import software.amazon.smithy.rust.codegen.util.getTrait -import java.lang.StringBuilder import java.util.logging.Logger // Use a sigil that should always be unique in the text to fix line breaks and spaces @@ -26,7 +26,7 @@ private const val SPACE_SIGIL = "[[smithy-rs-nbsp]]" /** * Generates a README.md for each service crate for display on crates.io. */ -class AwsReadmeDecorator : RustCodegenDecorator { +class AwsReadmeDecorator : RustCodegenDecorator { override val name: String = "AwsReadmeDecorator" override val order: Byte = 0 @@ -89,6 +89,8 @@ class AwsReadmeDecorator : RustCodegenDecorator { } } + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) + /** * Strips HTML from the description and makes it human-readable Markdown. */ diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CrateLicenseDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CrateLicenseDecorator.kt index f49139718e..81bfed4d92 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CrateLicenseDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CrateLicenseDecorator.kt @@ -6,11 +6,12 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.rust.codegen.rustlang.raw +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator -class CrateLicenseDecorator : RustCodegenDecorator { +class CrateLicenseDecorator : RustCodegenDecorator { override val name: String = "CrateLicense" override val order: Byte = 0 @@ -21,4 +22,6 @@ class CrateLicenseDecorator : RustCodegenDecorator { it.raw(license) } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index a40f151424..fd5a8db49c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -22,7 +23,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfig -class CredentialsProviderDecorator : RustCodegenDecorator { +class CredentialsProviderDecorator : RustCodegenDecorator { override val name: String = "CredentialsProvider" override val order: Byte = 0 @@ -47,6 +48,8 @@ class CredentialsProviderDecorator : RustCodegenDecorator { ): List { return baseCustomizations + PubUseCredentials(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } /** diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 8a03e1d820..867d437a51 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.rustlang.CratesIo import software.amazon.smithy.rust.codegen.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator @@ -18,7 +19,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection import java.nio.file.Files import java.nio.file.Paths -class IntegrationTestDecorator : RustCodegenDecorator { +class IntegrationTestDecorator : RustCodegenDecorator { override val name: String = "IntegrationTest" override val order: Byte = 0 @@ -47,6 +48,8 @@ class IntegrationTestDecorator : RustCodegenDecorator { baseCustomizations } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class IntegrationTestDependencies( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index f2e139a352..70be487d74 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -69,7 +70,7 @@ fn test_1() { } */ -class RegionDecorator : RustCodegenDecorator { +class RegionDecorator : RustCodegenDecorator { override val name: String = "Region" override val order: Byte = 0 @@ -94,6 +95,8 @@ class RegionDecorator : RustCodegenDecorator { ): List { return baseCustomizations + PubUseRegion(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryPolicyDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryPolicyDecorator.kt index 94f365b0e4..2186363e95 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryPolicyDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryPolicyDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -16,7 +17,7 @@ import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomizati import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator -class RetryPolicyDecorator : RustCodegenDecorator { +class RetryPolicyDecorator : RustCodegenDecorator { override val name: String = "RetryPolicy" override val order: Byte = 0 @@ -27,6 +28,8 @@ class RetryPolicyDecorator : RustCodegenDecorator { ): List { return baseCustomizations + RetryPolicyFeature(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class RetryPolicyFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 934c0542c9..71223bb22c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustCrate @@ -23,7 +24,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfi * - `From<&aws_types::SdkConfig> for ::config::Builder`: Enabling customization * - `pub fn new(&aws_types::SdkConfig) -> ::Config`: Direct construction without customization */ -class SdkConfigDecorator : RustCodegenDecorator { +class SdkConfigDecorator : RustCodegenDecorator { override val name: String = "SdkConfig" override val order: Byte = 0 @@ -66,6 +67,8 @@ class SdkConfigDecorator : RustCodegenDecorator { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class NewFromShared(runtimeConfig: RuntimeConfig) : ConfigCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/ServiceConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/ServiceConfigDecorator.kt index 0b19698047..223b75872b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/ServiceConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/ServiceConfigDecorator.kt @@ -8,12 +8,13 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfig -class ServiceConfigDecorator : RustCodegenDecorator { +class ServiceConfigDecorator : RustCodegenDecorator { override val name: String = "ServiceConfigGenerator" override val order: Byte = 0 @@ -21,6 +22,8 @@ class ServiceConfigDecorator : RustCodegenDecorator { codegenContext: CodegenContext, baseCustomizations: List ): List = baseCustomizations + SharedConfigDocsCustomization() + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class SharedConfigDocsCustomization : ConfigCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 935789068a..36e6f685ae 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -42,7 +43,7 @@ import software.amazon.smithy.rust.codegen.util.isInputEventStream * - sets a default `OperationSigningConfig` A future enhancement will customize this for specific services that need * different behavior. */ -class SigV4SigningDecorator : RustCodegenDecorator { +class SigV4SigningDecorator : RustCodegenDecorator { override val name: String = "SigV4Signing" override val order: Byte = 0 @@ -75,6 +76,8 @@ class SigV4SigningDecorator : RustCodegenDecorator { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class SigV4SigningConfig( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index e24bfd1774..d25afa2e7b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -28,7 +29,7 @@ import software.amazon.smithy.rust.codegen.util.expectTrait /** * Inserts a UserAgent configuration into the operation */ -class UserAgentDecorator : RustCodegenDecorator { +class UserAgentDecorator : RustCodegenDecorator { override val name: String = "UserAgent" override val order: Byte = 10 @@ -55,6 +56,8 @@ class UserAgentDecorator : RustCodegenDecorator { ): List { return baseCustomizations + UserAgentFeature(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } /** diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 7a168a5b5e..663c858b11 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization @@ -17,7 +18,7 @@ import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.letIf -class ApiGatewayDecorator : RustCodegenDecorator { +class ApiGatewayDecorator : RustCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 @@ -31,6 +32,8 @@ class ApiGatewayDecorator : RustCodegenDecorator { it + ApiGatewayAddAcceptHeader() } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class ApiGatewayAddAcceptHeader : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt index 65c4957c13..df5b84ff69 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt @@ -11,11 +11,12 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator private fun String.shapeId() = ShapeId.from(this) // / STS (and possibly other services) need to have auth manually set to [] -class DisabledAuthDecorator : RustCodegenDecorator { +class DisabledAuthDecorator : RustCodegenDecorator { override val name: String = "OptionalAuth" override val order: Byte = 0 @@ -44,4 +45,6 @@ class DisabledAuthDecorator : RustCodegenDecorator { } } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt index 70ae2f9940..b0c6c18e04 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt @@ -8,10 +8,11 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.letIf -class Ec2Decorator : RustCodegenDecorator { +class Ec2Decorator : RustCodegenDecorator { override val name: String = "Ec2" override val order: Byte = 0 private val ec2 = ShapeId.from("com.amazonaws.ec2#AmazonEC2") @@ -27,4 +28,6 @@ class Ec2Decorator : RustCodegenDecorator { BoxPrimitiveShapes::processModel ) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index d6badd3812..31cf79ed6e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -7,13 +7,14 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator val Glacier: ShapeId = ShapeId.from("com.amazonaws.glacier#Glacier") -class GlacierDecorator : RustCodegenDecorator { +class GlacierDecorator : RustCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 @@ -35,4 +36,6 @@ class GlacierDecorator : RustCodegenDecorator { } return baseCustomizations + extras } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index 7a0338fb4f..1969d89908 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization @@ -28,7 +29,7 @@ import java.util.logging.Logger val Route53: ShapeId = ShapeId.from("com.amazonaws.route53#AWSDnsV20130401") -class Route53Decorator : RustCodegenDecorator { +class Route53Decorator : RustCodegenDecorator { override val name: String = "Route53" override val order: Byte = 0 private val logger: Logger = Logger.getLogger(javaClass.name) @@ -59,6 +60,8 @@ class Route53Decorator : RustCodegenDecorator { private fun isHostId(shape: Shape): Boolean { return (shape is MemberShape && shape.target == ShapeId.from("com.amazonaws.route53#ResourceId")) && shape.hasTrait() } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class TrimHostedZoneCustomization(private val fieldName: String) : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index e5417ce6b9..d8067a28be 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator @@ -30,31 +31,33 @@ import software.amazon.smithy.rustsdk.AwsRuntimeType /** * Top level decorator for S3 * */ -class S3Decorator : RustCodegenDecorator { +class S3Decorator : RustCodegenDecorator { override val name: String = "S3ExtendedError" override val order: Byte = 0 private fun applies(serviceId: ShapeId) = serviceId == ShapeId.from("com.amazonaws.s3#AmazonS3") - override fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap { - return currentProtocols.letIf(applies(serviceId)) { + override fun protocols( + serviceId: ShapeId, + currentProtocols: ProtocolMap + ): ProtocolMap = + currentProtocols.letIf(applies(serviceId)) { it + mapOf( RestXmlTrait.ID to RestXmlFactory { protocolConfig -> S3(protocolConfig) } ) } - } override fun libRsCustomizations( codegenContext: CodegenContext, baseCustomizations: List - ): List { - return baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { - it + S3PubUse() - } + ): List = baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { + it + S3PubUse() } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class S3(codegenContext: CodegenContext) : RestXml(codegenContext) { diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustCodegenServerPlugin.kt index 1bd6ac5405..c37ab917cf 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustCodegenServerPlugin.kt @@ -12,9 +12,10 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWordSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations import software.amazon.smithy.rust.codegen.smithy.BaseSymbolMetadataProvider -import software.amazon.smithy.rust.codegen.smithy.DefaultConfig import software.amazon.smithy.rust.codegen.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.StreamingShapeMetadataProvider import software.amazon.smithy.rust.codegen.smithy.StreamingShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitor @@ -41,7 +42,8 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { // - location (e.g. the mutate section of an operation) // - context (e.g. the of the operation) // - writer: The active RustWriter at the given location - val codegenDecorator = CombinedCodegenDecorator.fromClasspath(context) + val codegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(context, ServerRequiredCustomizations()) // ServerCodegenVisitor is the main driver of code generation that traverses the model and generates code logger.info("Loaded plugin to generate Rust/Python bindings for the server SSDK") @@ -58,7 +60,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { fun baseSymbolProvider( model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig + symbolVisitorConfig: SymbolVisitorConfig, ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // Generate different types for EventStream shapes (e.g. transcribe streaming) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 2223a2cc37..f2bf5642e1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -11,9 +11,10 @@ import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWordSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations import software.amazon.smithy.rust.codegen.smithy.BaseSymbolMetadataProvider -import software.amazon.smithy.rust.codegen.smithy.DefaultConfig import software.amazon.smithy.rust.codegen.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.StreamingShapeMetadataProvider import software.amazon.smithy.rust.codegen.smithy.StreamingShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitor @@ -37,12 +38,13 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { override fun execute(context: PluginContext) { // Suppress extremely noisy logs about reserved words Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF - // Discover [RustCodegenDecorators] on the classpath. [RustCodegenDecorator] return different types of - // customization. A customization is a function of: + // Discover [RustCodegenDecorators] on the classpath. [RustCodegenDecorator] returns different types of + // customizations. A customization is a function of: // - location (e.g. the mutate section of an operation) // - context (e.g. the of the operation) // - writer: The active RustWriter at the given location - val codegenDecorator = CombinedCodegenDecorator.fromClasspath(context) + val codegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(context, ServerRequiredCustomizations()) // ServerCodegenVisitor is the main driver of code generation that traverses the model and generates code logger.info("Loaded plugin to generate pure Rust bindings for the server SSDK") @@ -59,12 +61,18 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { fun baseSymbolProvider( model: Model, serviceShape: ServiceShape, - symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig, + symbolVisitorConfig: SymbolVisitorConfig, publicConstrainedTypesEnabled: Boolean = true ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs - .let { if (publicConstrainedTypesEnabled) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + .let { + if (publicConstrainedTypesEnabled) ConstrainedShapeSymbolProvider( + it, + model, + serviceShape + ) else it + } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 7dda7b76a5..0d62250ccb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -35,7 +35,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader -import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.Constrained import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.ModelsModule @@ -43,6 +42,8 @@ import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbol import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.Unconstrained import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -54,9 +55,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory -import software.amazon.smithy.rust.codegen.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.smithy.transformers.RecursiveShapeBoxer @@ -71,7 +70,10 @@ import java.util.logging.Logger * Entrypoint for server-side code generation. This class will walk the in-memory model and * generate all the needed types by calling the accept() function on the available shapes. */ -class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: RustCodegenDecorator) : +class ServerCodegenVisitor( + context: PluginContext, + private val codegenDecorator: RustCodegenDecorator +) : ShapeVisitor.Default() { private val logger = Logger.getLogger(javaClass.name) @@ -84,8 +86,8 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model - private val codegenContext: CodegenContext - private val protocolGeneratorFactory: ProtocolGeneratorFactory + private val codegenContext: ServerCodegenContext + private val protocolGeneratorFactory: ProtocolGeneratorFactory private val protocolGenerator: ProtocolGenerator private val unconstrainedModule = RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") @@ -97,8 +99,9 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: val symbolVisitorConfig = SymbolVisitorConfig( runtimeConfig = settings.runtimeConfig, - codegenConfig = settings.codegenConfig, - handleRequired = true + renameExceptions = false, + handleRequired = true, + handleRustBoxing = true, ) val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) @@ -132,7 +135,7 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) - codegenContext = CodegenContext( + codegenContext = ServerCodegenContext( model, symbolProvider, service, @@ -160,8 +163,6 @@ class ServerCodegenVisitor(context: PluginContext, private val codegenDecorator: .let { ModelTransformer.create().copyServiceErrorsToOperations(it, settings.getService(it)) } // Add `Box` to recursive shapes as necessary .let(RecursiveShapeBoxer::transform) - // Normalize the `message` field on errors when enabled in settings (default: true) - .letIf(settings.codegenConfig.addMessageToErrors, AddErrorMessage::transform) // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) // Drop unsupported event stream operations from the model diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt deleted file mode 100644 index 998b04b058..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.server.smithy - -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.node.ObjectNode -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.rust.codegen.smithy.CODEGEN_SETTINGS -import software.amazon.smithy.rust.codegen.smithy.CodegenConfig -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.smithy.RustSettings -import java.util.Optional - -/** - * Configuration of codegen settings - * - * [renameExceptions]: Rename `Exception` to `Error` in the generated SDK - * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to false - * and generates its own client) - * - * [addMessageToErrors]: Adds a `message` field automatically to all error shapes - * [formatTimeoutSeconds]: Timeout for running cargo fmt at the end of code generation - */ -data class ServerCodegenConfig( - val renameExceptions: Boolean = false, - val includeFluentClient: Boolean = false, - val addMessageToErrors: Boolean = false, - val formatTimeoutSeconds: Int = 20, - val debugMode: Boolean = false, - // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services - val eventStreamAllowList: Set = emptySet(), -) { - companion object { - fun fromNode(node: Optional): CodegenConfig { - return if (node.isPresent) { - CodegenConfig.fromNode(node) - } else { - CodegenConfig( - false, - false, - false, - 20, - debugMode = false, - emptySet() - ) - } - } - } -} - -/** - * Settings used by [RustCodegenPlugin] - */ -class ServerRustSettings( - val service: ShapeId, - val moduleName: String, - val moduleVersion: String, - val moduleAuthors: List, - val moduleDescription: String?, - val moduleRepository: String?, - val runtimeConfig: RuntimeConfig, - val codegenConfig: CodegenConfig, - val license: String?, - val examplesUri: String? = null, - private val model: Model -) { - companion object { - /** - * Create settings from a configuration object node. - * - * @param model Model to infer the service from (if not explicitly set in config) - * @param config Config object to load - * @throws software.amazon.smithy.model.node.ExpectationNotMetException - * @return Returns the extracted settings - */ - fun from(model: Model, config: ObjectNode): RustSettings { - val codegenSettings = config.getObjectMember(CODEGEN_SETTINGS) - val codegenConfig = ServerCodegenConfig.fromNode(codegenSettings) - return RustSettings.fromCodegenConfig(model, config, codegenConfig) - } - } -} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt index 0038af876b..df5030663d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator /** @@ -29,12 +30,14 @@ import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator * mkdir -p "$D" && echo "$C" > "$D/$F" * ``` */ -class AddInternalServerErrorToInfallibleOpsDecorator : RustCodegenDecorator { +class AddInternalServerErrorToInfallibleOpsDecorator : RustCodegenDecorator { override val name: String = "AddInternalServerErrorToInfallibleOps" override val order: Byte = 0 override fun transformModel(service: ServiceShape, model: Model): Model = - addErrorShapeToModelOps(service, model, { shape -> shape.errors.isEmpty() }) + addErrorShapeToModelOps(service, model) { shape -> shape.errors.isEmpty() } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(CodegenContext::class.java) } /** @@ -55,16 +58,18 @@ class AddInternalServerErrorToInfallibleOpsDecorator : RustCodegenDecorator { * mkdir -p "$D" && echo "$C" > "$D/$F" * ``` */ -class AddInternalServerErrorToAllOpsDecorator : RustCodegenDecorator { +class AddInternalServerErrorToAllOpsDecorator : RustCodegenDecorator { override val name: String = "AddInternalServerErrorToAllOps" override val order: Byte = 0 override fun transformModel(service: ServiceShape, model: Model): Model = - addErrorShapeToModelOps(service, model, { _ -> true }) + addErrorShapeToModelOps(service, model) { true } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(CodegenContext::class.java) } -fun addErrorShapeToModelOps(service: ServiceShape, model: Model, opSelector: (OperationShape) -> Boolean): Model { - val errorShape = internalServerError(service.id.getNamespace()) +private fun addErrorShapeToModelOps(service: ServiceShape, model: Model, opSelector: (OperationShape) -> Boolean): Model { + val errorShape = internalServerError(service.id.namespace) val modelShapes = model.toBuilder().addShapes(listOf(errorShape)).build() return ModelTransformer.create().mapShapes(modelShapes) { shape -> if (shape is OperationShape && opSelector(shape)) { @@ -75,7 +80,7 @@ fun addErrorShapeToModelOps(service: ServiceShape, model: Model, opSelector: (Op } } -fun internalServerError(namespace: String): StructureShape = +private fun internalServerError(namespace: String): StructureShape = StructureShape.builder().id("$namespace#InternalServerError") .addTrait(ErrorTrait("server")) .addMember( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt new file mode 100644 index 0000000000..8025059323 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.customizations + +import software.amazon.smithy.rust.codegen.rustlang.Feature +import software.amazon.smithy.rust.codegen.smithy.RustCrate +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.customizations.AllowLintsGenerator +import software.amazon.smithy.rust.codegen.smithy.customizations.CrateVersionGenerator +import software.amazon.smithy.rust.codegen.smithy.customizations.SmithyTypesPubUseGenerator +import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization + +/** + * A set of customizations that are included in all protocols. + * + * This exists as a convenient place to gather these modifications, these are not true customizations. + */ +class ServerRequiredCustomizations : RustCodegenDecorator { + override val name: String = "Required" + override val order: Byte = -1 + + // TODO Convert ServerCodegenContext into CodegenContext and depend on RequiredCustomizations.kt + + override fun libRsCustomizations( + codegenContext: ServerCodegenContext, + baseCustomizations: List + ): List = + baseCustomizations + CrateVersionGenerator() + SmithyTypesPubUseGenerator(codegenContext.runtimeConfig) + AllowLintsGenerator() + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + // Add rt-tokio feature for `ByteStream::from_path` + rustCrate.mergeFeature(Feature("rt-tokio", true, listOf("aws-smithy-http/rt-tokio"))) + } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ServerCodegenContext::class.java) +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index d48ffc3c25..f96055ea4a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJsonVersion @@ -30,10 +31,11 @@ import software.amazon.smithy.rust.codegen.util.hasTrait * AwsJson 1.0 and 1.1 server-side protocol factory. This factory creates the [ServerHttpBoundProtocolGenerator] * with AwsJson specific configurations. */ -class ServerAwsJsonFactory(private val version: AwsJsonVersion) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = ServerAwsJson(codegenContext, version) +class ServerAwsJsonFactory(private val version: AwsJsonVersion) : + ProtocolGeneratorFactory { + override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerAwsJson(codegenContext, version) - override fun buildProtocolGenerator(codegenContext: CodegenContext): ServerHttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = ServerHttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 44ec8b7e8a..2f5368f014 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -43,8 +43,8 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerRequestBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator -import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol @@ -80,7 +80,7 @@ import java.util.logging.Logger * See `ServerRestJsonFactory.kt` for more info. */ class ServerHttpBoundProtocolGenerator( - codegenContext: CodegenContext, + codegenContext: ServerCodegenContext, protocol: Protocol, ) : ProtocolGenerator( codegenContext, @@ -106,12 +106,12 @@ class ServerHttpBoundProtocolGenerator( * non-streaming types. */ private class ServerHttpBoundProtocolTraitImplGenerator( - private val codegenContext: CodegenContext, + private val codegenContext: ServerCodegenContext, private val protocol: Protocol, ) : ProtocolTraitImplGenerator { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider - private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider!! + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver @@ -1051,7 +1051,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ServerRequestBindingGenerator( protocol, codegenContext, - codegenContext.unconstrainedShapeSymbolProvider!!, + codegenContext.unconstrainedShapeSymbolProvider, operationShape, ) val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) @@ -1071,7 +1071,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ServerRequestBindingGenerator( protocol, codegenContext, - codegenContext.unconstrainedShapeSymbolProvider!!, + codegenContext.unconstrainedShapeSymbolProvider, operationShape, ) val deserializer = httpBindingGenerator.generateDeserializePrefixHeadersFn(binding) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerProtocolLoader.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerProtocolLoader.kt index 09de8308c0..67929355ff 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerProtocolLoader.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerProtocolLoader.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.Trait +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory @@ -23,11 +24,11 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolMap /* * Protocol dispatcher, responsible for protocol selection. */ -class ServerProtocolLoader(private val supportedProtocols: ProtocolMap) { +class ServerProtocolLoader(private val supportedProtocols: ProtocolMap) { fun protocolFor( model: Model, serviceShape: ServiceShape - ): Pair> { + ): Pair> { val protocols: MutableMap = ServiceIndex.of(model).getProtocols(serviceShape) val matchingProtocols = protocols.keys.mapNotNull { protocolId -> supportedProtocols[protocolId]?.let { protocolId to it } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt index dc5e543a09..9d226b7e6c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols import software.amazon.smithy.model.Model -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory @@ -16,10 +16,10 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.RestJson * RestJson1 server-side protocol factory. This factory creates the [ServerHttpProtocolGenerator] * with RestJson1 specific configurations. */ -class ServerRestJsonFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = RestJson(codegenContext) +class ServerRestJsonFactory : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ServerCodegenContext): Protocol = RestJson(codegenContext) - override fun buildProtocolGenerator(codegenContext: CodegenContext): ServerHttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = ServerHttpBoundProtocolGenerator(codegenContext, RestJson(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestXmlFactory.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestXmlFactory.kt index f22b1886a2..e45247ff8d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestXmlFactory.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestXmlFactory.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols import software.amazon.smithy.model.Model -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory @@ -16,10 +16,10 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.RestXml * RestXml server-side protocol factory. This factory creates the [ServerHttpProtocolGenerator] * with RestXml specific configurations. */ -class ServerRestXmlFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = RestXml(codegenContext) +class ServerRestXmlFactory : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ServerCodegenContext): Protocol = RestXml(codegenContext) - override fun buildProtocolGenerator(codegenContext: CodegenContext): ServerHttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = ServerHttpBoundProtocolGenerator(codegenContext, RestXml(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 17d8ef748a..19b0ab8db4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -12,46 +12,48 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator -import software.amazon.smithy.rust.codegen.smithy.CodegenConfig -import software.amazon.smithy.rust.codegen.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.smithy.RustSettings +import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenConfig +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.testutil.testRustSettings +// These are the settings we default to if the user does not override them in their `smithy-build.json`. val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( runtimeConfig = TestRuntimeConfig, - // These are the settings we default to if the user does not override them in their `smithy-build.json`. - codegenConfig = CodegenConfig( - renameExceptions = false, - includeFluentClient = false, - addMessageToErrors = false, - formatTimeoutSeconds = 20, - eventStreamAllowList = emptySet() - ), + renameExceptions = false, handleRustBoxing = true, handleRequired = true ) -fun serverTestCodegenContext( - model: Model, - serviceShape: ServiceShape? = null, - settings: RustSettings = testRustSettings(), - codegenTarget: CodegenTarget = CodegenTarget.SERVER -): CodegenContext = CodegenContext( - model, - serverTestSymbolProvider(model), - TestRuntimeConfig, - serviceShape - ?: model.serviceShapes.firstOrNull() - ?: ServiceShape.builder().version("test").id("test#Service").build(), - ShapeId.from("test#Protocol"), - settings, - codegenTarget +fun serverTestRustSettings( + service: ShapeId = ShapeId.from("notrelevant#notrelevant"), + moduleName: String = "test-module", + moduleVersion: String = "notrelevant", + moduleAuthors: List = listOf("notrelevant"), + moduleDescription: String = "not relevant", + moduleRepository: String? = null, + runtimeConfig: RuntimeConfig = TestRuntimeConfig, + codegenConfig: ServerCodegenConfig = ServerCodegenConfig(), + license: String? = null, + examplesUri: String? = null, +) = ServerRustSettings( + service, + moduleName, + moduleVersion, + moduleAuthors, + moduleDescription, + moduleRepository, + runtimeConfig, + codegenConfig, + license, + examplesUri ) fun serverTestSymbolProvider( @@ -66,6 +68,29 @@ fun serverTestSymbolProvider( publicConstrainedTypesEnabled = publicConstrainedTypesEnabled ) +fun serverTestCodegenContext( + model: Model, + serviceShape: ServiceShape? = null, + settings: ServerRustSettings = serverTestRustSettings(), + codegenTarget: CodegenTarget = CodegenTarget.SERVER +): ServerCodegenContext { + val service = + serviceShape + ?: model.serviceShapes.firstOrNull() + ?: ServiceShape.builder().version("test").id("test#Service").build() + val symbolProvider = serverTestSymbolProvider(model, serviceShape) + val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + return ServerCodegenContext( + model, + symbolProvider, + service, + ShapeId.from("test#Protocol"), + settings, + codegenTarget, + unconstrainedShapeSymbolProvider + ) +} + /** * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt index ac108a9d00..d4d152d411 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt @@ -13,80 +13,66 @@ import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget /** * Configuration needed to generate the client or the server for a given (Service, Protocol) pair. */ -data class CodegenContext( +// TODO Rename to CoreCodegenContext +open class CodegenContext( /** * The smithy model. * * Note: This model may or not be pruned to the given service closure, so ensure that `serviceShape` is used as * an entry point. */ - val model: Model, + open val model: Model, - val symbolProvider: RustSymbolProvider, + open val symbolProvider: RustSymbolProvider, - /** - * Configuration of the runtime package: - * - Where are the runtime crates (smithy-*) located on the file system? Or are they versioned? - * - What are they called? - */ - val runtimeConfig: RuntimeConfig, /** * Entrypoint service shape for code generation */ - val serviceShape: ServiceShape, + open val serviceShape: ServiceShape, /** * Smithy Protocol to generate, e.g. RestJson1 */ - val protocol: ShapeId, + open val protocol: ShapeId, /** * Settings loaded from smithy-build.json */ - val settings: RustSettings, + open val settings: RustSettings, + /** * Server vs. Client codegen * * Some settings are dependent on whether server vs. client codegen is being invoked. */ - val target: CodegenTarget, - - /** - * This is nullable as only the server needs it. - * - * TODO: I think the time has come for us to have separate classes: - * - A `ClientCodegenContext` holding `RustSettings`. - * - A `ServerCodegenContext` holding `ServerRustSettings`, `UnconstrainedShapeSymbolProvider`. - * - A `CoreCodegenContext` held by the two classes above for the common properties. - * This "split" would also happen in: - * - `RustSettings`: `ClientRustSettings`, `ServerRustSettings`, `CoreRustSettings`. - * - `CodegenConfig`: `ClientCodegenConfig`, `ServerCodegenConfig`, `CoreCodegenConfig`. - * This would mean generators will only be able to rely on the `Core*` classes: - * - if they just need to know who they're generating for, `CodegenTarget` should be passed in separately. - * - if they additionally need other things that pertain only to the client or only to the server, they - * should be passed in separately in a bigger enum-like class encapsulating it. - */ - val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null, + open val target: CodegenTarget, ) { - constructor( - model: Model, - symbolProvider: RustSymbolProvider, - serviceShape: ServiceShape, - protocol: ShapeId, - settings: RustSettings, - target: CodegenTarget, - unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider? = null - ) : this(model, symbolProvider, settings.runtimeConfig, serviceShape, protocol, settings, target, unconstrainedShapeSymbolProvider) + + // TODO This is just a convenience: we should remove this property and refactor all code to use the `runtimeConfig` + // inside `settings` directly. + val runtimeConfig: RuntimeConfig by lazy { settings.runtimeConfig } /** * The name of the cargo crate to generate e.g. `aws-sdk-s3` * This is loaded from the smithy-build.json during codegen. */ + // TODO This is just a convenience: we should remove this property and refactor all code to use the `moduleName` + // inside `settings` directly. val moduleName: String by lazy { settings.moduleName } /** * A moduleName for a crate uses kebab-case. When you want to `use` a crate in Rust code, * it must be in snake-case. Call this method to get this crate's name in snake-case. */ - fun moduleUseName(): String { - return this.moduleName.replace("-", "_") - } + fun moduleUseName() = moduleName.replace("-", "_") } + +data class ClientCodegenContext( + override val model: Model, + override val symbolProvider: RustSymbolProvider, + override val serviceShape: ServiceShape, + override val protocol: ShapeId, + override val settings: ClientRustSettings, + override val target: CodegenTarget, +) : CodegenContext( + model, symbolProvider, serviceShape, protocol, settings, target +) + diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt index 04bdfe849c..e2f64cbecb 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt @@ -42,23 +42,28 @@ import java.util.logging.Logger /** * Base Entrypoint for Code generation */ -class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustCodegenDecorator) : +class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustCodegenDecorator) : ShapeVisitor.Default() { private val logger = Logger.getLogger(javaClass.name) - private val settings = RustSettings.from(context.model, context.settings) + private val settings = ClientRustSettings.from(context.model, context.settings) private val symbolProvider: RustSymbolProvider private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model - private val codegenContext: CodegenContext - private val protocolGenerator: ProtocolGeneratorFactory + private val codegenContext: ClientCodegenContext + private val protocolGenerator: ProtocolGeneratorFactory private val httpGenerator: ProtocolGenerator init { val symbolVisitorConfig = - SymbolVisitorConfig(runtimeConfig = settings.runtimeConfig, codegenConfig = settings.codegenConfig) + SymbolVisitorConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = settings.codegenConfig.renameExceptions, + handleRequired = false, + handleRustBoxing = true, + ) val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) val (protocol, generator) = ProtocolLoader( @@ -69,7 +74,7 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC val baseProvider = RustCodegenPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) symbolProvider = codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) - codegenContext = CodegenContext(model, symbolProvider, service, protocol, settings, target = CodegenTarget.CLIENT) + codegenContext = ClientCodegenContext(model, symbolProvider, service, protocol, settings, target = CodegenTarget.CLIENT) rustCrate = RustCrate( context.fileManifest, symbolProvider, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt index ceecac0b30..0ae06925cb 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt @@ -14,13 +14,16 @@ import software.amazon.smithy.rust.codegen.rustlang.Attribute.Companion.NonExhau import software.amazon.smithy.rust.codegen.rustlang.RustReservedWordSymbolProvider import software.amazon.smithy.rust.codegen.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.customize.RequiredCustomizations import java.util.logging.Level import java.util.logging.Logger -/** Rust Codegen Plugin - * This is the entrypoint for code generation, triggered by the smithy-build plugin. - * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which - * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. +/** + * Rust Codegen Plugin + * + * This is the entrypoint for code generation, triggered by the smithy-build plugin. + * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which + * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. */ class RustCodegenPlugin : SmithyBuildPlugin { override fun getName(): String = "rust-codegen" @@ -28,12 +31,13 @@ class RustCodegenPlugin : SmithyBuildPlugin { override fun execute(context: PluginContext) { // Suppress extremely noisy logs about reserved words Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF - // Discover `RustCodegenDecorators` on the classpath. `RustCodegenDecorator` return different types of - // customization. A customization is a function of: + // Discover `RustCodegenDecorators` on the classpath. `RustCodegenDecorator` returns different types of + // customizations. A customization is a function of: // - location (e.g. the mutate section of an operation) // - context (e.g. the of the operation) // - writer: The active RustWriter at the given location - val codegenDecorator = CombinedCodegenDecorator.fromClasspath(context, ClientCustomizations()) + val codegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(context, ClientCustomizations(), RequiredCustomizations()) // CodegenVisitor is the main driver of code generation that traverses the model and generates code CodegenVisitor(context, codegenDecorator).execute() @@ -46,7 +50,7 @@ class RustCodegenPlugin : SmithyBuildPlugin { * The Symbol provider is composed of a base `SymbolVisitor` which handles the core functionality, then is layered * with other symbol providers, documented inline, to handle the full scope of Smithy types. */ - fun baseSymbolProvider(model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig = DefaultConfig) = + fun baseSymbolProvider(model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustSettings.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustSettings.kt index ab3007d940..68d0f1e926 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustSettings.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustSettings.kt @@ -13,77 +13,58 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.util.orNull -import java.util.Optional +import java.util.* import java.util.logging.Logger import kotlin.streams.toList -private const val SERVICE = "service" -private const val MODULE_NAME = "module" -private const val MODULE_DESCRIPTION = "moduleDescription" -private const val MODULE_VERSION = "moduleVersion" -private const val MODULE_AUTHORS = "moduleAuthors" -private const val MODULE_REPOSITORY = "moduleRepository" -private const val RUNTIME_CONFIG = "runtimeConfig" -private const val LICENSE = "license" -private const val EXAMPLES = "examples" -private const val CUSTOMIZATION_CONFIG = "customizationConfig" +const val SERVICE = "service" +const val MODULE_NAME = "module" +const val MODULE_DESCRIPTION = "moduleDescription" +const val MODULE_VERSION = "moduleVersion" +const val MODULE_AUTHORS = "moduleAuthors" +const val MODULE_REPOSITORY = "moduleRepository" +const val RUNTIME_CONFIG = "runtimeConfig" +const val LICENSE = "license" +const val EXAMPLES = "examples" +const val CUSTOMIZATION_CONFIG = "customizationConfig" const val CODEGEN_SETTINGS = "codegen" /** - * Configuration of codegen settings + * Configuration of codegen settings. * * [renameExceptions]: Rename `Exception` to `Error` in the generated SDK - * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to false - * and generates its own client) - * - * [addMessageToErrors]: Adds a `message` field automatically to all error shapes * [formatTimeoutSeconds]: Timeout for running cargo fmt at the end of code generation + * [debugMode]: Generate comments in the generated code indicating where code was generated from */ -data class CodegenConfig( - val renameExceptions: Boolean = true, - val includeFluentClient: Boolean = true, - val addMessageToErrors: Boolean = true, - val formatTimeoutSeconds: Int = 20, - /** Generate comments in the generated code indicating where code was generated from */ - val debugMode: Boolean = false, +// TODO Remove the default params, they should only be needed by TestSymbolVisitorDefaultConfig +open class CodegenConfig( + open val formatTimeoutSeconds: Int = 20, + open val debugMode: Boolean = false, // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services - val eventStreamAllowList: Set = emptySet(), -) { - companion object { - fun fromNode(node: Optional): CodegenConfig { - return if (node.isPresent) { - CodegenConfig( - node.get().getBooleanMemberOrDefault("renameErrors", true), - node.get().getBooleanMemberOrDefault("includeFluentClient", true), - node.get().getBooleanMemberOrDefault("addMessageToErrors", true), - node.get().getNumberMemberOrDefault("formatTimeoutSeconds", 20).toInt(), - node.get().getBooleanMemberOrDefault("debugMode", false), - node.get().getArrayMember("eventStreamAllowList") - .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } - .orNull()?.toSet() ?: emptySet() - ) - } else { - CodegenConfig() - } - } - } -} + open val eventStreamAllowList: Set = emptySet(), +) /** * Settings used by [RustCodegenPlugin] */ -class RustSettings( - val service: ShapeId, - val moduleName: String, - val moduleVersion: String, - val moduleAuthors: List, - val moduleDescription: String?, - val moduleRepository: String?, - val runtimeConfig: RuntimeConfig, - val codegenConfig: CodegenConfig, - val license: String?, - val examplesUri: String? = null, - val customizationConfig: ObjectNode? = null +open class RustSettings( + open val service: ShapeId, + open val moduleName: String, + open val moduleVersion: String, + open val moduleAuthors: List, + open val moduleDescription: String?, + open val moduleRepository: String?, + + /** + * Configuration of the runtime package: + * - Where are the runtime crates (smithy-*) located on the file system? Or are they versioned? + * - What are they called? + */ + open val runtimeConfig: RuntimeConfig, + open val codegenConfig: CodegenConfig, + open val license: String?, + open val examplesUri: String? = null, + open val customizationConfig: ObjectNode? = null ) { /** @@ -102,17 +83,64 @@ class RustSettings( companion object { private val LOGGER: Logger = Logger.getLogger(RustSettings::class.java.name) + // Infer the service to generate from a model. + @JvmStatic + protected fun inferService(model: Model): ShapeId { + val services = model.shapes(ServiceShape::class.java) + .map(Shape::getId) + .sorted() + .toList() + + when { + services.isEmpty() -> { + throw CodegenException( + "Cannot infer a service to generate because the model does not " + + "contain any service shapes" + ) + } + services.size > 1 -> { + throw CodegenException( + "Cannot infer service to generate because the model contains " + + "multiple service shapes: " + services + ) + } + else -> { + val service = services[0] + LOGGER.info("Inferring service to generate as: $service") + return service + } + } + } + } +} + +data class ClientRustSettings( + override val service: ShapeId, + override val moduleName: String, + override val moduleVersion: String, + override val moduleAuthors: List, + override val moduleDescription: String?, + override val moduleRepository: String?, + override val runtimeConfig: RuntimeConfig, + override val codegenConfig: ClientCodegenConfig, + override val license: String?, + override val examplesUri: String? = null, + override val customizationConfig: ObjectNode? = null +): RustSettings( + service, moduleName, moduleVersion, moduleAuthors, moduleDescription, moduleRepository, runtimeConfig, codegenConfig, license +) { + + companion object { /** * Create settings from a configuration object node. * * @param model Model to infer the service from (if not explicitly set in config) * @param config Config object to load - * @throws software.amazon.smithy.model.node.ExpectationNotMetException * @return Returns the extracted settings */ - fun from(model: Model, config: ObjectNode): RustSettings { + fun from(model: Model, config: ObjectNode): ClientRustSettings { val codegenSettings = config.getObjectMember(CODEGEN_SETTINGS) - val codegenConfig = CodegenConfig.fromNode(codegenSettings) + val codegenConfig = ClientCodegenConfig.fromNode(codegenSettings) return fromCodegenConfig(model, config, codegenConfig) } @@ -122,10 +150,9 @@ class RustSettings( * @param model Model to infer the service from (if not explicitly set in config) * @param config Config object to load * @param codegenConfig CodegenConfig object to use - * @throws software.amazon.smithy.model.node.ExpectationNotMetException * @return Returns the extracted settings */ - fun fromCodegenConfig(model: Model, config: ObjectNode, codegenConfig: CodegenConfig): RustSettings { + private fun fromCodegenConfig(model: Model, config: ObjectNode, codegenConfig: ClientCodegenConfig): ClientRustSettings { config.warnIfAdditionalProperties( arrayListOf( SERVICE, @@ -147,8 +174,8 @@ class RustSettings( .orElseGet { inferService(model) } val runtimeConfig = config.getObjectMember(RUNTIME_CONFIG) - return RustSettings( - service = service, + return ClientRustSettings( + service, moduleName = config.expectStringMember(MODULE_NAME).value, moduleVersion = config.expectStringMember(MODULE_VERSION).value, moduleAuthors = config.expectArrayMember(MODULE_AUTHORS).map { it.expectStringNode().value }, @@ -161,33 +188,39 @@ class RustSettings( customizationConfig = config.getObjectMember(CUSTOMIZATION_CONFIG).orNull() ) } + } +} - // infer the service to generate from a model - private fun inferService(model: Model): ShapeId { - val services = model.shapes(ServiceShape::class.java) - .map(Shape::getId) - .sorted() - .toList() - - when { - services.isEmpty() -> { - throw CodegenException( - "Cannot infer a service to generate because the model does not " + - "contain any service shapes" - ) - } - services.size > 1 -> { - throw CodegenException( - "Cannot infer service to generate because the model contains " + - "multiple service shapes: " + services - ) - } - else -> { - val service = services[0] - LOGGER.info("Inferring service to generate as: $service") - return service - } +/** + * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to `false` + * and generates its own client) + * [addMessageToErrors]: Adds a `message` field automatically to all error shapes + */ +data class ClientCodegenConfig( + override val formatTimeoutSeconds: Int = 20, + override val debugMode: Boolean = false, + override val eventStreamAllowList: Set = emptySet(), + val renameExceptions: Boolean = true, + val includeFluentClient: Boolean = true, + val addMessageToErrors: Boolean = true, +): CodegenConfig( + formatTimeoutSeconds, debugMode, eventStreamAllowList +) { + companion object { + fun fromNode(node: Optional): ClientCodegenConfig = + if (node.isPresent) { + ClientCodegenConfig( + node.get().getNumberMemberOrDefault("formatTimeoutSeconds", 20).toInt(), + node.get().getBooleanMemberOrDefault("debugMode", false), + node.get().getArrayMember("eventStreamAllowList") + .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } + .orNull()?.toSet() ?: emptySet(), + node.get().getBooleanMemberOrDefault("includeFluentClient", true), + node.get().getBooleanMemberOrDefault("addMessageToErrors", true), + node.get().getBooleanMemberOrDefault("renameErrors", true), + ) + } else { + ClientCodegenConfig() } - } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt new file mode 100644 index 0000000000..8f290aabc6 --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt @@ -0,0 +1,24 @@ +package software.amazon.smithy.rust.codegen.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget + +/** + * TODO Docs + * + * This class has to live in the `codegen` subproject because it is referenced in common generators to both client + * and server (like [JsonParserGenerator]). + */ +data class ServerCodegenContext( + override val model: Model, + override val symbolProvider: RustSymbolProvider, + override val serviceShape: ServiceShape, + override val protocol: ShapeId, + override val settings: ServerRustSettings, + override val target: CodegenTarget, + val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider +) : CodegenContext( + model, symbolProvider, serviceShape, protocol, settings, target +) \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt new file mode 100644 index 0000000000..1880de45bd --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt @@ -0,0 +1,124 @@ +package software.amazon.smithy.rust.codegen.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.util.orNull +import java.util.* + +// TODO from* functions are copied from RustSettings.kt + +/** + * [ServerRustSettings] and [ServerCodegenConfig] classes. + * + * These classes are entirely analogous to [ClientRustSettings] and [ClientCodegenConfig]. Refer to the documentation + * for those. + * + * These classes have to live in the `codegen` subproject because they are referenced in [ServerCodegenContext], + * which is used in common generators to both client and server (like [JsonParserGenerator]). + */ + +data class ServerRustSettings( + override val service: ShapeId, + override val moduleName: String, + override val moduleVersion: String, + override val moduleAuthors: List, + override val moduleDescription: String?, + override val moduleRepository: String?, + override val runtimeConfig: RuntimeConfig, + override val codegenConfig: ServerCodegenConfig, + override val license: String?, + override val examplesUri: String? = null, + override val customizationConfig: ObjectNode? = null +): RustSettings( + service, moduleName, moduleVersion, moduleAuthors, moduleDescription, moduleRepository, runtimeConfig, codegenConfig, license +) { + companion object { + /** + * Create settings from a configuration object node. + * + * @param model Model to infer the service from (if not explicitly set in config) + * @param config Config object to load + * @return Returns the extracted settings + */ + fun from(model: Model, config: ObjectNode): ServerRustSettings { + val codegenSettings = config.getObjectMember(CODEGEN_SETTINGS) + val codegenConfig = ServerCodegenConfig.fromNode(codegenSettings) + return fromCodegenConfig(model, config, codegenConfig) + } + + /** + * Create settings from a configuration object node and CodegenConfig. + * + * @param model Model to infer the service from (if not explicitly set in config) + * @param config Config object to load + * @param codegenConfig CodegenConfig object to use + * @return Returns the extracted settings + */ + fun fromCodegenConfig( + model: Model, + config: ObjectNode, + codegenConfig: ServerCodegenConfig + ): ServerRustSettings { + config.warnIfAdditionalProperties( + arrayListOf( + SERVICE, + MODULE_NAME, + MODULE_DESCRIPTION, + MODULE_AUTHORS, + MODULE_VERSION, + MODULE_REPOSITORY, + RUNTIME_CONFIG, + CODEGEN_SETTINGS, + EXAMPLES, + LICENSE, + CUSTOMIZATION_CONFIG + ) + ) + + val service = config.getStringMember(SERVICE) + .map(StringNode::expectShapeId) + .orElseGet { inferService(model) } + + val runtimeConfig = config.getObjectMember(RUNTIME_CONFIG) + return ServerRustSettings( + service, + moduleName = config.expectStringMember(MODULE_NAME).value, + moduleVersion = config.expectStringMember(MODULE_VERSION).value, + moduleAuthors = config.expectArrayMember(MODULE_AUTHORS).map { it.expectStringNode().value }, + moduleDescription = config.getStringMember(MODULE_DESCRIPTION).orNull()?.value, + moduleRepository = config.getStringMember(MODULE_REPOSITORY).orNull()?.value, + runtimeConfig = RuntimeConfig.fromNode(runtimeConfig), + codegenConfig, + license = config.getStringMember(LICENSE).orNull()?.value, + examplesUri = config.getStringMember(EXAMPLES).orNull()?.value, + customizationConfig = config.getObjectMember(CUSTOMIZATION_CONFIG).orNull() + ) + } + } +} + +data class ServerCodegenConfig( + override val formatTimeoutSeconds: Int = 20, + override val debugMode: Boolean = false, + override val eventStreamAllowList: Set = emptySet(), +): CodegenConfig( + formatTimeoutSeconds, debugMode, eventStreamAllowList +) { + companion object { + // TODO It'd be nice if we could load the common properties and then just load the server ones here. Same for `ClientCodegenConfig`. + fun fromNode(node: Optional): ServerCodegenConfig = + if (node.isPresent) { + ServerCodegenConfig( + node.get().getNumberMemberOrDefault("formatTimeoutSeconds", 20).toInt(), + node.get().getBooleanMemberOrDefault("debugMode", false), + node.get().getArrayMember("eventStreamAllowList") + .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } + .orNull()?.toSet() ?: emptySet() + ) + } else { + ServerCodegenConfig() + } + } +} \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 01db052a91..90e0e923f7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -62,19 +62,11 @@ val SimpleShapes: Map, RustType> = mapOf( data class SymbolVisitorConfig( val runtimeConfig: RuntimeConfig, - val codegenConfig: CodegenConfig, - val handleRustBoxing: Boolean = true, - val handleRequired: Boolean = false + val renameExceptions: Boolean, + val handleRustBoxing: Boolean, + val handleRequired: Boolean, ) -val DefaultConfig = - SymbolVisitorConfig( - runtimeConfig = RuntimeConfig(), - handleRustBoxing = true, - handleRequired = false, - codegenConfig = CodegenConfig() - ) - /** * Container type for the file a symbol should be written to * @@ -218,7 +210,7 @@ fun Shape.contextName(serviceShape: ServiceShape?): String { class SymbolVisitor( private val model: Model, private val serviceShape: ServiceShape?, - private val config: SymbolVisitorConfig = DefaultConfig + private val config: SymbolVisitorConfig ) : RustSymbolProvider, ShapeVisitor { private val nullableIndex = NullableIndex.of(model) @@ -330,7 +322,7 @@ class SymbolVisitor( val isError = shape.hasTrait() val isInput = shape.hasTrait() val isOutput = shape.hasTrait() - val name = shape.contextName(serviceShape).toPascalCase().letIf(isError && config.codegenConfig.renameExceptions) { + val name = shape.contextName(serviceShape).toPascalCase().letIf(isError && config.renameExceptions) { it.replace("Exception", "Error") } val builder = symbolBuilder(shape, RustType.Opaque(name)) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt index 89140ea7f8..ce9cf6c44e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt @@ -42,7 +42,11 @@ val AllowDocsLints = listOf( "bare_urls" ) -class AllowLintsGenerator(private val bareLints: List = listOf(), private val clippyLints: List = ClippyAllowLints, private val docsLints: List = AllowDocsLints) : LibRsCustomization() { +class AllowLintsGenerator( + private val bareLints: List = listOf(), + private val clippyLints: List = ClippyAllowLints, + private val docsLints: List = AllowDocsLints +) : LibRsCustomization() { override fun section(section: LibRsSection) = when (section) { is LibRsSection.Attributes -> writable { bareLints.forEach { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientCustomizations.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientCustomizations.kt index dbbda7bd9c..ac37ebbce5 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientCustomizations.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientCustomizations.kt @@ -5,21 +5,21 @@ package software.amazon.smithy.rust.codegen.smithy.customizations -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization /** - * Customizations that apply only to generated clients + * Customizations that apply only to generated clients. */ -class ClientCustomizations : RustCodegenDecorator { +class ClientCustomizations : RustCodegenDecorator { override val name: String = "ClientCustomizations" override val order: Byte = 0 override fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, baseCustomizations: List - ): List { - return baseCustomizations + ClientDocsGenerator() - } + ): List = baseCustomizations + ClientDocsGenerator() + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientDocsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientDocsGenerator.kt index a31295a6cc..49ee0170c6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientDocsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/ClientDocsGenerator.kt @@ -20,11 +20,10 @@ class ClientDocsGenerator : LibRsCustomization() { else -> emptySection } } -} -private fun crateLayout(): Writable = writable { - containerDocs( - """ + private fun crateLayout(): Writable = writable { + containerDocs( + """ The entry point for most customers will be [`Client`]. [`Client`] exposes one method for each API offered by the service. @@ -35,5 +34,6 @@ private fun crateLayout(): Writable = writable { The other modules within this crate are not required for normal usage. """.trimEnd() - ) + ) + } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/DocsRsMetadataDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/DocsRsMetadataDecorator.kt index 26f2a496be..a63c80a471 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/DocsRsMetadataDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/DocsRsMetadataDecorator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.smithy.customizations +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations @@ -48,11 +49,13 @@ fun DocsRsMetadataSettings.asMap(): Map { * # Notes * This decorator is not used by default, code generators must manually configure and include it in their builds. */ -class DocsRsMetadataDecorator(private val docsRsMetadataSettings: DocsRsMetadataSettings) : RustCodegenDecorator { +class DocsRsMetadataDecorator(private val docsRsMetadataSettings: DocsRsMetadataSettings) : RustCodegenDecorator { override val name: String = "docsrs-metadata" override val order: Byte = 0 override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations { return docsRsMetadataSettings.asMap() } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt index e3f1be3606..b74b45cbf7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -66,7 +67,7 @@ fn test_1() { } */ -class RetryConfigDecorator : RustCodegenDecorator { +class RetryConfigDecorator : RustCodegenDecorator { override val name: String = "RetryConfig" override val order: Byte = 0 @@ -78,11 +79,13 @@ class RetryConfigDecorator : RustCodegenDecorator { } override fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, baseCustomizations: List ): List { return baseCustomizations + PubUseRetryConfig(codegenContext.runtimeConfig) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class RetryConfigProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt index 9f68b63cbe..5f7bd7d249 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/SleepImplDecorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.smithy.customizations import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -115,7 +116,7 @@ impl Builder { } */ -class SleepImplDecorator : RustCodegenDecorator { +class SleepImplDecorator : RustCodegenDecorator { override val name: String = "AsyncSleep" override val order: Byte = 0 @@ -125,6 +126,8 @@ class SleepImplDecorator : RustCodegenDecorator { ): List { return baseCustomizations + SleepImplProviderConfig(codegenContext) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class SleepImplProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt index 10da009094..bf9b8e73e3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/TimeoutConfigDecorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.smithy.customizations import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator @@ -102,7 +103,7 @@ fn test_1() { } */ -class TimeoutConfigDecorator : RustCodegenDecorator { +class TimeoutConfigDecorator : RustCodegenDecorator { override val name: String = "TimeoutConfig" override val order: Byte = 0 @@ -112,6 +113,8 @@ class TimeoutConfigDecorator : RustCodegenDecorator { ): List { return baseCustomizations + TimeoutConfigProviderConfig(codegenContext) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } class TimeoutConfigProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt index dafb9a0aa6..6c29b20aa2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RequiredCustomizations.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.smithy.customize import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.rustlang.Feature +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customizations.AllowLintsGenerator @@ -18,11 +19,12 @@ import software.amazon.smithy.rust.codegen.smithy.customizations.IdempotencyToke import software.amazon.smithy.rust.codegen.smithy.customizations.SmithyTypesPubUseGenerator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization -/** A set of customizations that are included in all protocols. +/** + * A set of customizations that are included in all protocols. * * This exists as a convenient place to gather these modifications, these are not true customizations. */ -class RequiredCustomizations : RustCodegenDecorator { +class RequiredCustomizations : RustCodegenDecorator { override val name: String = "Required" override val order: Byte = -1 @@ -30,23 +32,23 @@ class RequiredCustomizations : RustCodegenDecorator { codegenContext: CodegenContext, operation: OperationShape, baseCustomizations: List - ): List { - return baseCustomizations + - IdempotencyTokenGenerator(codegenContext, operation) + - EndpointPrefixGenerator(codegenContext, operation) + - HttpChecksumRequiredGenerator(codegenContext, operation) + - HttpVersionListCustomization(codegenContext, operation) - } + ): List = + baseCustomizations + + IdempotencyTokenGenerator(codegenContext, operation) + + EndpointPrefixGenerator(codegenContext, operation) + + HttpChecksumRequiredGenerator(codegenContext, operation) + + HttpVersionListCustomization(codegenContext, operation) override fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, baseCustomizations: List - ): List { - return baseCustomizations + CrateVersionGenerator() + SmithyTypesPubUseGenerator(codegenContext.runtimeConfig) + AllowLintsGenerator() - } + ): List = + baseCustomizations + CrateVersionGenerator() + SmithyTypesPubUseGenerator(codegenContext.runtimeConfig) + AllowLintsGenerator() - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { // Add rt-tokio feature for `ByteStream::from_path` rustCrate.mergeFeature(Feature("rt-tokio", true, listOf("aws-smithy-http/rt-tokio"))) } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(ClientCodegenContext::class.java) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt index c17e765aa0..672e542378 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt @@ -15,11 +15,11 @@ import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations -import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolMap +import software.amazon.smithy.rust.codegen.util.PANIC import software.amazon.smithy.rust.codegen.util.deepMergeWith -import java.util.ServiceLoader +import java.util.* import java.util.logging.Logger /** @@ -29,7 +29,7 @@ import java.util.logging.Logger * AWS services. A different downstream customer may wish to add a different set of derive * attributes to the generated classes. */ -interface RustCodegenDecorator { +interface RustCodegenDecorator { /** * The name of this [RustCodegenDecorator], used for logging and debug information */ @@ -45,6 +45,7 @@ interface RustCodegenDecorator { baseCustomizations: List ): List = baseCustomizations + // TODO Can we make this exist only for Client decorators? fun operationCustomizations( codegenContext: CodegenContext, operation: OperationShape, @@ -52,7 +53,7 @@ interface RustCodegenDecorator { ): List = baseCustomizations fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: C, baseCustomizations: List ): List = baseCustomizations @@ -63,13 +64,17 @@ interface RustCodegenDecorator { */ fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = emptyMap() - fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) {} + fun extras(codegenContext: C, rustCrate: RustCrate) {} - fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap = currentProtocols + fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap = + currentProtocols fun transformModel(service: ServiceShape, model: Model): Model = model fun symbolProvider(baseProvider: RustSymbolProvider): RustSymbolProvider = baseProvider + + // TODO Can we remove this and instead in the filter catch ClassCast Exception? + fun canOperateWithCodegenContext(t: Class<*>): Boolean } /** @@ -77,20 +82,20 @@ interface RustCodegenDecorator { * * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ -open class CombinedCodegenDecorator(decorators: List) : RustCodegenDecorator { +open class CombinedCodegenDecorator(decorators: List>) : RustCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } override val name: String get() = "MetaDecorator" override val order: Byte get() = 0 - fun withDecorator(decorator: RustCodegenDecorator) = CombinedCodegenDecorator(orderedDecorators + decorator) + fun withDecorator(decorator: RustCodegenDecorator) = CombinedCodegenDecorator(orderedDecorators + decorator) override fun configCustomizations( codegenContext: CodegenContext, baseCustomizations: List ): List { - return orderedDecorators.foldRight(baseCustomizations) { decorator: RustCodegenDecorator, customizations -> + return orderedDecorators.foldRight(baseCustomizations) { decorator: RustCodegenDecorator, customizations -> decorator.configCustomizations(codegenContext, customizations) } } @@ -100,13 +105,13 @@ open class CombinedCodegenDecorator(decorators: List) : Ru operation: OperationShape, baseCustomizations: List ): List { - return orderedDecorators.foldRight(baseCustomizations) { decorator: RustCodegenDecorator, customizations -> + return orderedDecorators.foldRight(baseCustomizations) { decorator: RustCodegenDecorator, customizations -> decorator.operationCustomizations(codegenContext, operation, customizations) } } override fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: C, baseCustomizations: List ): List { return orderedDecorators.foldRight(baseCustomizations) { decorator, customizations -> @@ -117,7 +122,7 @@ open class CombinedCodegenDecorator(decorators: List) : Ru } } - override fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap { + override fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap { return orderedDecorators.foldRight(currentProtocols) { decorator, protocolMap -> decorator.protocols(serviceId, protocolMap) } @@ -135,7 +140,7 @@ open class CombinedCodegenDecorator(decorators: List) : Ru } } - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + override fun extras(codegenContext: C, rustCrate: RustCrate) { return orderedDecorators.forEach { it.extras(codegenContext, rustCrate) } } @@ -145,17 +150,33 @@ open class CombinedCodegenDecorator(decorators: List) : Ru } } + override fun canOperateWithCodegenContext(t: Class<*>): Boolean { + PANIC("You have forgotten to override this method in your class that subclasses `CombinedCodegenDecorator`.") + } + companion object { - private val logger = Logger.getLogger("RustCodegenSPILoader") - fun fromClasspath(context: PluginContext, vararg extras: RustCodegenDecorator): CombinedCodegenDecorator { + inline fun fromClasspathGeneric( + context: PluginContext, + vararg extras: RustCodegenDecorator, + logger: Logger = Logger.getLogger("RustCodegenSPILoader") + ): CombinedCodegenDecorator { val decorators = ServiceLoader.load( RustCodegenDecorator::class.java, context.pluginClassLoader.orElse(RustCodegenDecorator::class.java.classLoader) ) + .filter { it.canOperateWithCodegenContext(T::class.java) } .onEach { logger.info("Adding Codegen Decorator: ${it.javaClass.name}") - }.toList() - return CombinedCodegenDecorator(decorators + RequiredCustomizations() + FluentClientDecorator() + extras) + } + .map { + // Cast is safe because of the filter above. + @Suppress("UNCHECKED_CAST") + it as RustCodegenDecorator + } + .toList() + // TODO Should probably look like this. +// return CombinedCodegenDecorator(decorators + RequiredCustomizations() + extras) + return CombinedCodegenDecorator(decorators + extras) } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt index 092f427556..5771a0ec85 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.smithy.generators import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator @@ -30,7 +31,7 @@ class ServiceGenerator( private val protocolGenerator: ProtocolGenerator, private val protocolSupport: ProtocolSupport, private val config: CodegenContext, - private val decorator: RustCodegenDecorator, + private val decorator: RustCodegenDecorator, ) { private val index = TopDownIndex.of(config.model) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt index 2884ceb381..04677bcc75 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt @@ -37,6 +37,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustCrate @@ -59,14 +60,14 @@ import software.amazon.smithy.rust.codegen.util.orNull import software.amazon.smithy.rust.codegen.util.outputShape import software.amazon.smithy.rust.codegen.util.toSnakeCase -class FluentClientDecorator : RustCodegenDecorator { +class FluentClientDecorator : RustCodegenDecorator { override val name: String = "FluentClient" override val order: Byte = 0 - private fun applies(codegenContext: CodegenContext): Boolean = - codegenContext.symbolProvider.config().codegenConfig.includeFluentClient + private fun applies(codegenContext: ClientCodegenContext): Boolean = + codegenContext.settings.codegenConfig.includeFluentClient - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (!applies(codegenContext)) { return } @@ -80,7 +81,7 @@ class FluentClientDecorator : RustCodegenDecorator { } override fun libRsCustomizations( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, baseCustomizations: List ): List { if (!applies(codegenContext)) { @@ -96,6 +97,8 @@ class FluentClientDecorator : RustCodegenDecorator { } } } + + override fun canOperateWithCodegenContext(t: Class<*>) = t.isAssignableFrom(CodegenContext::class.java) } sealed class FluentClientSection(name: String) : Section(name) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsJson.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsJson.kt index ec14d709f9..2db12fd2dd 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsJson.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsJson.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport @@ -39,12 +40,11 @@ sealed class AwsJsonVersion { } } -class AwsJsonFactory(private val version: AwsJsonVersion) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = AwsJson(codegenContext, version) +class AwsJsonFactory(private val version: AwsJsonVersion) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = AwsJson(codegenContext, version) - override fun buildProtocolGenerator(codegenContext: CodegenContext): HttpBoundProtocolGenerator { - return HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) - } + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = + HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsQuery.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsQuery.kt index 1ed6b95c51..31baf2d309 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsQuery.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/AwsQuery.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport @@ -26,10 +27,10 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.AwsQuerySe import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.util.getTrait -class AwsQueryFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = AwsQueryProtocol(codegenContext) +class AwsQueryFactory : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = AwsQueryProtocol(codegenContext) - override fun buildProtocolGenerator(codegenContext: CodegenContext): HttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Ec2Query.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Ec2Query.kt index f010e97ed7..6e15d91d83 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Ec2Query.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Ec2Query.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport @@ -23,10 +24,10 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.parse.StructuredData import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.Ec2QuerySerializerGenerator import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.StructuredDataSerializerGenerator -class Ec2QueryFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = Ec2QueryProtocol(codegenContext) +class Ec2QueryFactory : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = Ec2QueryProtocol(codegenContext) - override fun buildProtocolGenerator(codegenContext: CodegenContext): HttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt index c59236840d..f355a0d99c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt @@ -76,21 +76,22 @@ interface Protocol { fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType } -typealias ProtocolMap = Map> +typealias ProtocolMap = Map> -interface ProtocolGeneratorFactory { - fun protocol(codegenContext: CodegenContext): Protocol - fun buildProtocolGenerator(codegenContext: CodegenContext): T +interface ProtocolGeneratorFactory { + fun protocol(codegenContext: C): Protocol + fun buildProtocolGenerator(codegenContext: C): T fun transformModel(model: Model): Model fun symbolProvider(model: Model, base: RustSymbolProvider): RustSymbolProvider = base fun support(): ProtocolSupport } -class ProtocolLoader(private val supportedProtocols: ProtocolMap) { +class ProtocolLoader(private val supportedProtocols: ProtocolMap) { + // TODO Is this function used? fun protocolFor( model: Model, serviceShape: ServiceShape - ): Pair> { + ): Pair> { val protocols: MutableMap = ServiceIndex.of(model).getProtocols(serviceShape) val matchingProtocols = protocols.keys.mapNotNull { protocolId -> supportedProtocols[protocolId]?.let { protocolId to it } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt index 596880dc41..5519a191b3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport @@ -29,10 +30,10 @@ import software.amazon.smithy.rust.codegen.util.getTrait import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.outputShape -class RestJsonFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = RestJson(codegenContext) +class RestJsonFactory : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) - override fun buildProtocolGenerator(codegenContext: CodegenContext): HttpBoundProtocolGenerator = + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = HttpBoundProtocolGenerator(codegenContext, RestJson(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestXml.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestXml.kt index 98e9702a74..ab230dc653 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestXml.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestXml.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport @@ -23,11 +24,13 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.Structured import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.XmlBindingTraitSerializerGenerator import software.amazon.smithy.rust.codegen.util.expectTrait -class RestXmlFactory(private val generator: (CodegenContext) -> Protocol = { RestXml(it) }) : - ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol = generator(codegenContext) +class RestXmlFactory( + private val generator: (ClientCodegenContext) -> Protocol = { RestXml(it) } +) : ProtocolGeneratorFactory { - override fun buildProtocolGenerator(codegenContext: CodegenContext): HttpBoundProtocolGenerator = + override fun protocol(codegenContext: ClientCodegenContext): Protocol = generator(codegenContext) + + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): HttpBoundProtocolGenerator = HttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) override fun transformModel(model: Model): Model = model diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index df2403a9a2..dfc19007cc 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.smithy.protocols.parse +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.CollectionShape @@ -36,7 +37,7 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget @@ -61,15 +62,13 @@ import software.amazon.smithy.utils.StringUtils // and the function is declared `pub`, Rust will complain, even if the json_deser module is not `pub`. class JsonParserGenerator( - codegenContext: CodegenContext, + private val codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, ) : StructuredDataParserGenerator { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider - // TODO Grab it - private val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, codegenContext.serviceShape) private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target private val smithyJson = CargoDependency.smithyJson(runtimeConfig).asType() @@ -286,7 +285,7 @@ class JsonParserGenerator( withBlock("$escapedStrName.to_unescaped().map(|u|", ")") { when (target.hasTrait()) { true -> { - if (parseUnconstrainedShape(target)) { + if (returnSymbolToParse(target).first) { rust("u.into_owned()") } else { rust("#T::from(u.as_ref())", symbolProvider.toSymbol(target)) @@ -321,12 +320,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeCollection(shape: CollectionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = parseUnconstrainedShape(shape) - val returnType = if (returnUnconstrainedType) { - unconstrainedShapeSymbolProvider.toSymbol(shape) - } else { - symbolProvider.toSymbol(shape) - } + val (returnUnconstrainedType, returnSymbol) = returnSymbolToParse(shape) val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have lists with names prefixed with `__listOf__`, // which become `__list_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. @@ -336,7 +330,7 @@ class JsonParserGenerator( pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "ReturnType" to returnType, + "ReturnType" to returnSymbol, *codegenScope, ) { startArrayOrNull { @@ -363,7 +357,7 @@ class JsonParserGenerator( } } if (returnUnconstrainedType) { - rust("Ok(Some(#{T}(items)))", returnType) + rust("Ok(Some(#{T}(items)))", returnSymbol) } else { rust("Ok(Some(items))") } @@ -377,12 +371,7 @@ class JsonParserGenerator( val keyTarget = model.expectShape(shape.key.target) as StringShape val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val returnUnconstrainedType = parseUnconstrainedShape(shape) - val returnType = if (returnUnconstrainedType) { - unconstrainedShapeSymbolProvider.toSymbol(shape) - } else { - symbolProvider.toSymbol(shape) - } + val (returnUnconstrainedType, returnSymbol) = returnSymbolToParse(shape) val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have maps with names prefixed with `__mapOf__`, // which become `__map_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. @@ -392,7 +381,7 @@ class JsonParserGenerator( pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "ReturnType" to returnType, + "ReturnType" to returnSymbol, *codegenScope, ) { startObjectOrNull { @@ -413,7 +402,7 @@ class JsonParserGenerator( } } if (returnUnconstrainedType) { - rust("Ok(Some(#{T}(map)))", returnType) + rust("Ok(Some(#{T}(map)))", returnSymbol) } else { rust("Ok(Some(map))") } @@ -426,12 +415,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeStruct(shape: StructureShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val symbol = symbolProvider.toSymbol(shape) - val returnBuilder = codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) - val returnType = if (returnBuilder) { - unconstrainedShapeSymbolProvider.toSymbol(shape) - } else { - symbol - } + val (returnBuilder, returnType) = returnSymbolToParse(shape) val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ @@ -459,11 +443,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeUnion(shape: UnionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) - val symbol = if (parseUnconstrainedShape(shape)) { - unconstrainedShapeSymbolProvider.toSymbol(shape) - } else { - symbolProvider.toSymbol(shape) - } + val (_, symbol) = returnSymbolToParse(shape) val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { it.rustBlockTemplate( """ @@ -595,7 +575,14 @@ class JsonParserGenerator( * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the * user gets. This is only relevant for the server, that parses the incoming request and only after enforces * constraint traits. + * + * The function returns a pair where the second component is the return symbol that should be parsed, and the first + * component is whether such symbol is unconstrained or not. */ - private fun parseUnconstrainedShape(shape: Shape) = - codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider) + private fun returnSymbolToParse(shape: Shape): Pair = + if (codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider)) { + true to (codegenContext as ServerCodegenContext).unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + false to symbolProvider.toSymbol(shape) + } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt index e27cc75cb2..5c75b5bd8d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt @@ -21,12 +21,11 @@ import software.amazon.smithy.rust.codegen.rustlang.RustDependency import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.raw import software.amazon.smithy.rust.codegen.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.smithy.CodegenConfig +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenConfig import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.MaybeRenamed import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustCrate -import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.letIf @@ -91,6 +90,7 @@ object TestWorkspace { } } + // TODO This should not be used by the server because it's using ClientCodegenConfig. @Suppress("NAME_SHADOWING") fun testProject(symbolProvider: RustSymbolProvider? = null, debugMode: Boolean = false): TestWriterDelegator { val subprojectDir = subproject() @@ -110,7 +110,7 @@ object TestWorkspace { return TestWriterDelegator( FileManifest.create(subprojectDir.toPath()), symbolProvider, - CodegenConfig(debugMode = debugMode) + ClientCodegenConfig(debugMode = debugMode) ) } } @@ -176,7 +176,7 @@ fun RustWriter.unitTest( * * This exposes both the base directory and a list of [generatedFiles] for test purposes */ -class TestWriterDelegator(private val fileManifest: FileManifest, symbolProvider: RustSymbolProvider, val codegenConfig: CodegenConfig) : +class TestWriterDelegator(private val fileManifest: FileManifest, symbolProvider: RustSymbolProvider, val codegenConfig: ClientCodegenConfig) : RustCrate(fileManifest, symbolProvider, DefaultPublicModules, codegenConfig) { val baseDir: Path = fileManifest.baseDir @@ -221,16 +221,10 @@ fun TestWriterDelegator.compileAndTest(runClippy: Boolean = false) { } fun TestWriterDelegator.rustSettings() = - RustSettings( - ShapeId.from("fake#Fake"), - "test_${baseDir.toFile().nameWithoutExtension}", - "0.0.1", - moduleAuthors = listOf("test@module.com"), - moduleDescription = "test", - moduleRepository = null, - runtimeConfig = TestRuntimeConfig, - codegenConfig = this.codegenConfig, - license = null + testRustSettings( + service = ShapeId.from("fake#Fake"), + moduleName = "test_${baseDir.toFile().nameWithoutExtension}", + codegenConfig = this.codegenConfig ) fun String.shouldParseAsRust() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt index 7b05fd7e20..7cc5dad081 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestHelpers.kt @@ -15,12 +15,12 @@ import software.amazon.smithy.rust.codegen.rustlang.CratesIo import software.amazon.smithy.rust.codegen.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asType -import software.amazon.smithy.rust.codegen.smithy.CodegenConfig -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenConfig +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeCrateLocation import software.amazon.smithy.rust.codegen.smithy.RustCodegenPlugin -import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator @@ -35,22 +35,23 @@ val TestRuntimeConfig = RuntimeConfig(runtimeCrateLocation = RuntimeCrateLocation.Path(File("../rust-runtime/").absolutePath)) val TestSymbolVisitorConfig = SymbolVisitorConfig( runtimeConfig = TestRuntimeConfig, - codegenConfig = CodegenConfig(), - handleRustBoxing = true + renameExceptions = true, + handleRustBoxing = true, + handleRequired = false, ) fun testRustSettings( service: ShapeId = ShapeId.from("notrelevant#notrelevant"), moduleName: String = "test-module", - moduleVersion: String = "notrelevant", + moduleVersion: String = "0.0.1", moduleAuthors: List = listOf("notrelevant"), moduleDescription: String = "not relevant", moduleRepository: String? = null, - runtimeConfig: RuntimeConfig = RuntimeConfig(), - codegenConfig: CodegenConfig = CodegenConfig(), + runtimeConfig: RuntimeConfig = TestRuntimeConfig, + codegenConfig: ClientCodegenConfig = ClientCodegenConfig(), license: String? = null, examplesUri: String? = null, -) = RustSettings( +) = ClientRustSettings( service, moduleName, moduleVersion, @@ -73,12 +74,11 @@ fun testSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSy fun testCodegenContext( model: Model, serviceShape: ServiceShape? = null, - settings: RustSettings = testRustSettings(), + settings: ClientRustSettings = testRustSettings(), mode: CodegenTarget = CodegenTarget.CLIENT -): CodegenContext = CodegenContext( +): ClientCodegenContext = ClientCodegenContext( model, testSymbolProvider(model), - TestRuntimeConfig, serviceShape ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build(), diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt index 5165610be5..96a142b0ab 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt @@ -11,12 +11,14 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenVisitor import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.customize.RequiredCustomizations import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfig @@ -58,10 +60,11 @@ internal class HttpVersionListGeneratorTest { """.asSmithyModel() val (ctx, testDir) = generatePluginContext(model) val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') - val testWriter = object : RustCodegenDecorator { + val testWriter = object : RustCodegenDecorator { override val name: String = "add tests" override val order: Byte = 0 - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.withFile("tests/validate_defaults.rs") { TokioTest.render(it) it.rust( @@ -82,8 +85,12 @@ internal class HttpVersionListGeneratorTest { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } - val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(testWriter)) + val combinedCodegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(ctx, RequiredCustomizations()).withDecorator(testWriter) + val visitor = CodegenVisitor(ctx, combinedCodegenDecorator) visitor.execute() "cargo test".runCommand(testDir) } @@ -117,10 +124,11 @@ internal class HttpVersionListGeneratorTest { """.asSmithyModel() val (ctx, testDir) = generatePluginContext(model) val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') - val testWriter = object : RustCodegenDecorator { + val testWriter = object : RustCodegenDecorator { override val name: String = "add tests" override val order: Byte = 0 - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.withFile("tests/validate_http.rs") { TokioTest.render(it) it.rust( @@ -141,8 +149,13 @@ internal class HttpVersionListGeneratorTest { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } - val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(testWriter)) + + val combinedCodegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(ctx, RequiredCustomizations()).withDecorator(testWriter) + val visitor = CodegenVisitor(ctx, combinedCodegenDecorator) visitor.execute() "cargo test".runCommand(testDir) } @@ -189,7 +202,7 @@ internal class HttpVersionListGeneratorTest { val (ctx, testDir) = generatePluginContext(model, addModuleToEventStreamAllowList = true) val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') - val codegenDecorator = object : RustCodegenDecorator { + val codegenDecorator = object : RustCodegenDecorator { override val name: String = "add tests" override val order: Byte = 0 @@ -200,7 +213,7 @@ internal class HttpVersionListGeneratorTest { return super.configCustomizations(codegenContext, baseCustomizations) + FakeSigningConfig(codegenContext.runtimeConfig) } - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.withFile("tests/validate_eventstream_http.rs") { TokioTest.render(it) it.rust( @@ -220,9 +233,13 @@ internal class HttpVersionListGeneratorTest { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } - val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(codegenDecorator)) + val combinedCodegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(ctx, RequiredCustomizations()).withDecorator(codegenDecorator) + val visitor = CodegenVisitor(ctx, combinedCodegenDecorator) visitor.execute() "cargo test".runCommand(testDir) } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProviderTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProviderTest.kt index 0a85b92d1a..f05fcce5ad 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProviderTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProviderTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.testutil.TestSymbolVisitorConfig import software.amazon.smithy.rust.codegen.testutil.asSmithyModel class EventStreamSymbolProviderTest { @@ -41,7 +42,7 @@ class EventStreamSymbolProviderTest { ) val service = model.expectShape(ShapeId.from("test#TestService")) as ServiceShape - val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, DefaultConfig), model) + val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, TestSymbolVisitorConfig), model) // Look up the synthetic input/output rather than the original input/output val inputStream = model.expectShape(ShapeId.from("test.synthetic#TestOperationInput\$inputStream")) as MemberShape @@ -77,7 +78,7 @@ class EventStreamSymbolProviderTest { ) val service = model.expectShape(ShapeId.from("test#TestService")) as ServiceShape - val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, DefaultConfig), model) + val provider = EventStreamSymbolProvider(TestRuntimeConfig, SymbolVisitor(model, service, TestSymbolVisitorConfig), model) // Look up the synthetic input/output rather than the original input/output val inputStream = model.expectShape(ShapeId.from("test.synthetic#TestOperationInput\$inputStream")) as MemberShape diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt index bfc182ffd2..ba51c51071 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EndpointTraitBindingsTest.kt @@ -13,10 +13,11 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenVisitor import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.customize.CombinedCodegenDecorator +import software.amazon.smithy.rust.codegen.smithy.customize.RequiredCustomizations import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.testutil.TestWorkspace @@ -142,10 +143,11 @@ internal class EndpointTraitBindingsTest { """.asSmithyModel() val (ctx, testDir) = generatePluginContext(model) val moduleName = ctx.settings.expectStringMember("module").value.replace('-', '_') - val codegenDecorator = object : RustCodegenDecorator { + val codegenDecorator = object : RustCodegenDecorator { override val name: String = "add tests" override val order: Byte = 0 - override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) { + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.withFile("tests/validate_errors.rs") { TokioTest.render(it) it.rust( @@ -169,8 +171,12 @@ internal class EndpointTraitBindingsTest { ) } } + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } - val visitor = CodegenVisitor(ctx, CombinedCodegenDecorator.fromClasspath(ctx).withDecorator(codegenDecorator)) + val combinedCodegenDecorator: CombinedCodegenDecorator = + CombinedCodegenDecorator.fromClasspathGeneric(ctx, RequiredCustomizations()).withDecorator(codegenDecorator) + val visitor = CodegenVisitor(ctx, combinedCodegenDecorator) visitor.execute() "cargo test".runCommand(testDir) } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 1a71c17096..a4d28ff026 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.CodegenVisitor import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -102,12 +103,12 @@ private class TestProtocolFactory( private val httpRequestBuilder: String, private val body: String, private val correctResponse: String -) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: CodegenContext): Protocol { +) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol { return RestJson(codegenContext) } - override fun buildProtocolGenerator(codegenContext: CodegenContext): ProtocolGenerator { + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): ProtocolGenerator { return TestProtocolGenerator( codegenContext, protocol(codegenContext), @@ -211,7 +212,7 @@ class ProtocolTestGeneratorTest { private val correctBody = """{"name": "Teddy"}""" /** - * Creates an fake HTTP implementation for SayHello & generates the protocol test + * Creates a fake HTTP implementation for SayHello & generates the protocol test * * Returns the [Path] the service was generated at, suitable for running `cargo test` */ @@ -223,13 +224,17 @@ class ProtocolTestGeneratorTest { val (pluginContext, testDir) = generatePluginContext(model) val visitor = CodegenVisitor( pluginContext, - object : RustCodegenDecorator { + object : RustCodegenDecorator { override val name: String = "mock" override val order: Byte = 0 - override fun protocols(serviceId: ShapeId, currentProtocols: ProtocolMap): ProtocolMap { + override fun protocols( + serviceId: ShapeId, + currentProtocols: ProtocolMap + ): ProtocolMap = // Intentionally replace the builtin implementation of RestJson1 with our fake protocol - return mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) - } + mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) + + override fun canOperateWithCodegenContext(t: Class<*>): Boolean = t.isAssignableFrom(ClientCodegenContext::class.java) } ) visitor.execute() diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt index 4648241e6b..e4a1d76031 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.rustlang.rust -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestModels import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestTools @@ -24,10 +24,9 @@ class EventStreamUnmarshallerGeneratorTest { fun test(testCase: EventStreamTestModels.TestCase) { val test = EventStreamTestTools.generateTestProject(testCase) - val codegenContext = CodegenContext( + val codegenContext = ClientCodegenContext( test.model, test.symbolProvider, - TestRuntimeConfig, test.serviceShape, ShapeId.from(testCase.protocolShapeId), testRustSettings(), diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt index 8c5f33f9fc..924fe216bf 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/EventStreamMarshallerGeneratorTest.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestModels import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestTools import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig @@ -26,10 +26,9 @@ class EventStreamMarshallerGeneratorTest { fun test(testCase: EventStreamTestModels.TestCase) { val test = EventStreamTestTools.generateTestProject(testCase) - val codegenContext = CodegenContext( + val codegenContext = ClientCodegenContext( test.model, test.symbolProvider, - TestRuntimeConfig, test.serviceShape, ShapeId.from(testCase.protocolShapeId), testRustSettings(), diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/RemoveEventStreamOperationsTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/RemoveEventStreamOperationsTest.kt index 27a7da800c..9a1ebf4601 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/RemoveEventStreamOperationsTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/RemoveEventStreamOperationsTest.kt @@ -10,10 +10,10 @@ import io.kotest.matchers.shouldNotBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.rust.codegen.smithy.CodegenConfig +import software.amazon.smithy.rust.codegen.smithy.ClientCodegenConfig import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.testRustSettings -import java.util.Optional +import java.util.* internal class RemoveEventStreamOperationsTest { private val model = """ @@ -50,7 +50,7 @@ internal class RemoveEventStreamOperationsTest { val transformed = RemoveEventStreamOperations.transform( model, testRustSettings( - codegenConfig = CodegenConfig(eventStreamAllowList = setOf("not-test-module")), + codegenConfig = ClientCodegenConfig(eventStreamAllowList = setOf("not-test-module")), ) ) transformed.expectShape(ShapeId.from("test#BlobStream")) @@ -62,7 +62,7 @@ internal class RemoveEventStreamOperationsTest { val transformed = RemoveEventStreamOperations.transform( model, testRustSettings( - codegenConfig = CodegenConfig(eventStreamAllowList = setOf("test-module")), + codegenConfig = ClientCodegenConfig(eventStreamAllowList = setOf("test-module")), ) ) transformed.expectShape(ShapeId.from("test#BlobStream")) From 3fc9af4fa11f2f8e6f5686675caa1b7d83ae9136 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 6 Jul 2022 17:27:43 +0200 Subject: [PATCH 134/255] spst compiles but doesn't work --- .../smithy/rustsdk/AwsEndpointDecorator.kt | 1 + .../smithy/PythonServerCodegenVisitor.kt | 12 ++++++++++-- .../generators/PythonServerEnumGenerator.kt | 15 +++++++++++---- .../server/smithy/ServerCodegenVisitor.kt | 18 +++++++++--------- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt index 24351642b3..76a5a1574d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 6a4d79cbe2..e02b0ebf71 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -67,7 +67,8 @@ class PythonServerCodegenVisitor( codegenDecorator.symbolProvider(generator.symbolProvider(model, baseProvider)) // Override `codegenContext` which carries the symbolProvider. - codegenContext = ServerCodegenContext(model, symbolProvider, service, protocol, settings) + codegenContext = + ServerCodegenContext(model, symbolProvider, service, protocol, settings, unconstrainedShapeSymbolProvider) // Override `rustCrate` which carries the symbolProvider. rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) @@ -112,7 +113,14 @@ class PythonServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an enum $shape") shape.getTrait()?.also { enum -> rustCrate.useShapeWriter(shape) { writer -> - PythonServerEnumGenerator(model, symbolProvider, writer, shape, enum, codegenContext.runtimeConfig).render() + PythonServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + enum + ).render() } } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index cfa75f2a7b..f49646df09 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -17,8 +17,8 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.util.dq @@ -30,11 +30,18 @@ import software.amazon.smithy.rust.codegen.util.dq class PythonServerEnumGenerator( model: Model, symbolProvider: RustSymbolProvider, + constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val writer: RustWriter, - private val shape: StringShape, + shape: StringShape, enumTrait: EnumTrait, - runtimeConfig: RuntimeConfig, -) : ServerEnumGenerator(model, symbolProvider, writer, shape, enumTrait, runtimeConfig) { +) : ServerEnumGenerator( + model, + symbolProvider, + constraintViolationSymbolProvider, + writer, + shape, + enumTrait, +) { private val pyo3Symbols = listOf(PythonServerCargoDependency.PyO3.asType()) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 24042931fb..5cd084d358 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -77,16 +77,16 @@ open class ServerCodegenVisitor( protected val logger = Logger.getLogger(javaClass.name) protected val settings = ServerRustSettings.from(context.model, context.settings) - private val symbolProvider: RustSymbolProvider - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider + protected var symbolProvider: RustSymbolProvider + protected val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider - private val rustCrate: RustCrate + protected val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider + protected var rustCrate: RustCrate private val fileManifest = context.fileManifest - private val model: Model - private val codegenContext: ServerCodegenContext - private val protocolGeneratorFactory: ProtocolGeneratorFactory - private val protocolGenerator: ProtocolGenerator + protected var model: Model + protected var codegenContext: ServerCodegenContext + protected var protocolGeneratorFactory: ProtocolGeneratorFactory + protected var protocolGenerator: ProtocolGenerator private val unconstrainedModule = RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") private val constrainedModule = @@ -139,7 +139,7 @@ open class ServerCodegenVisitor( service, protocol, settings, - unconstrainedShapeSymbolProvider = unconstrainedShapeSymbolProvider + unconstrainedShapeSymbolProvider ) rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) From c2e2b56b557010841a0c56e71bc104e2abb912e5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 9 Aug 2022 21:29:28 +0200 Subject: [PATCH 135/255] save work --- codegen-server-test/build.gradle.kts | 5 ++- .../server/smithy/RustCodegenServerPlugin.kt | 4 +- .../server/smithy/ServerCodegenVisitor.kt | 42 +++++++++++++------ .../generators/ConstrainedMapGenerator.kt | 28 ++++++++++--- .../generators/ConstrainedStringGenerator.kt | 22 ++++++++-- .../generators/ServerBuilderGenerator.kt | 18 ++++++-- .../generators/UnconstrainedMapGenerator.kt | 3 +- .../smithy/testutil/ServerTestHelpers.kt | 7 ++-- .../rust/codegen/smithy/RuntimeTypes.kt | 2 + .../codegen/smithy/ServerCodegenContext.kt | 1 + .../rust/codegen/smithy/ServerRustSettings.kt | 29 +++++++++---- 11 files changed, 120 insertions(+), 41 deletions(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index c47c5a01d0..717be94a67 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -34,7 +34,10 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), - CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), + CodegenTest( + "com.amazonaws.simple#ConstraintsService", "constraints", + extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, + ), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 23ab83ac03..6102716858 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -63,12 +63,12 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - publicConstrainedTypesEnabled: Boolean = true + publicConstrainedTypes: Boolean = true ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs .let { - if (publicConstrainedTypesEnabled) ConstrainedShapeSymbolProvider( + if (publicConstrainedTypes) ConstrainedShapeSymbolProvider( it, model, serviceShape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 0905273170..d4e9eac4c5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -78,6 +78,7 @@ open class ServerCodegenVisitor( protected val settings = ServerRustSettings.from(context.model, context.settings) protected var symbolProvider: RustSymbolProvider + protected var constrainedShapeSymbolProvider: RustSymbolProvider protected val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider protected val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider @@ -113,13 +114,26 @@ open class ServerCodegenVisitor( .protocolFor(context.model, service) protocolGeneratorFactory = generator model = codegenDecorator.transformModel(service, baseModel) - symbolProvider = RustCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) + // TODO Can we make it work setting `publicConstrainedTypes` to `false`? + symbolProvider = RustCodegenServerPlugin.baseSymbolProvider( + model, + service, + symbolVisitorConfig, + publicConstrainedTypes = settings.codegenConfig.publicConstrainedTypes, + ) + constrainedShapeSymbolProvider = RustCodegenServerPlugin.baseSymbolProvider( + model, + service, + symbolVisitorConfig, + publicConstrainedTypes = true, + ) + // TODO Shouldn't `UnconstrainedShapeSymbolProvider` be applied at the same level as the `ConstrainedShapeSymbolProvider`? unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( RustCodegenServerPlugin.baseSymbolProvider( model, service, symbolVisitorConfig, - publicConstrainedTypesEnabled = false, + publicConstrainedTypes = false, ), model, service, ) @@ -132,7 +146,8 @@ open class ServerCodegenVisitor( service, protocol, settings, - unconstrainedShapeSymbolProvider + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, ) rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) @@ -284,6 +299,7 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, unconstrainedModuleWriter, shape @@ -307,12 +323,13 @@ open class ServerCodegenVisitor( val isDirectlyConstrained = shape.isDirectlyConstrained(symbolProvider) if (isDirectlyConstrained) { - rustCrate.useShapeWriter(shape) { writer -> + rustCrate.withModule(ModelsModule) { modelsModuleWriter -> ConstrainedMapGenerator( model, - symbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, - writer, + settings.codegenConfig.publicConstrainedTypes, + modelsModuleWriter, shape, if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null ).render() @@ -361,19 +378,20 @@ open class ServerCodegenVisitor( if (shape.hasTrait() && shape.isDirectlyConstrained(symbolProvider)) { logger.warning( """ - String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy - spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the - constraints (which it currently does not), not each code generator. - See https://github.com/awslabs/smithy/issues/1121f for more information. - """.trimIndent() + String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy + spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the + constraints (which it currently does not), not each code generator. + See https://github.com/awslabs/smithy/issues/1121f for more information. + """.trimIndent() ) } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> ConstrainedStringGenerator( model, - symbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, + settings.codegenConfig.publicConstrainedTypes, writer, shape ).render() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 12e95995d0..3264cdb739 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -9,7 +9,10 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider @@ -31,8 +34,9 @@ import software.amazon.smithy.rust.codegen.util.expectTrait */ class ConstrainedMapGenerator( val model: Model, - val symbolProvider: RustSymbolProvider, + private val constrainedSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val publicConstrainedTypes: Boolean, val writer: RustWriter, val shape: MapShape, private val unconstrainedSymbol: Symbol? = null, @@ -41,7 +45,7 @@ class ConstrainedMapGenerator( // The `length` trait is the only constraint trait applicable to map shapes. val lengthTrait = shape.expectTrait() - val name = symbolProvider.toSymbol(shape).name + val name = constrainedSymbolProvider.toSymbol(shape).name val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) @@ -53,11 +57,23 @@ class ConstrainedMapGenerator( "length <= ${lengthTrait.max.get()}" } + val constrainedTypeVisibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } + val constrainedTypeMetadata = RustMetadata( + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.Clone, RuntimeType.PartialEq)), + visibility = constrainedTypeVisibility + ) + + // TODO Display impl missing; it should honor `sensitive` trait. + writer.documentShape(shape, model, note = rustDocsNote(name)) + constrainedTypeMetadata.render(writer) writer.rustTemplate( """ - ##[derive(Debug, Clone, PartialEq)] - pub struct $name(pub(crate) $inner); + struct $name(pub(crate) $inner); impl $name { /// ${rustDocsParseMethod(name, inner)} @@ -89,8 +105,8 @@ class ConstrainedMapGenerator( } } """, - "KeySymbol" to symbolProvider.toSymbol(model.expectShape(shape.key.target)), - "ValueSymbol" to symbolProvider.toSymbol(model.expectShape(shape.value.target)), + "KeySymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.key.target)), + "ValueSymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.value.target)), "TryFrom" to RuntimeType.TryFrom, "ConstraintViolation" to constraintViolation ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 8dc179c50c..e802ce318e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -8,8 +8,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rustTemplate @@ -27,15 +30,16 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase */ class ConstrainedStringGenerator( val model: Model, - val symbolProvider: RustSymbolProvider, + private val constrainedSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val publicConstrainedTypes: Boolean, val writer: RustWriter, val shape: StringShape ) { fun render() { val lengthTrait = shape.expectTrait() - val symbol = symbolProvider.toSymbol(shape) + val symbol = constrainedSymbolProvider.toSymbol(shape) val name = symbol.name val inner = RustType.String.render() val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) @@ -48,15 +52,25 @@ class ConstrainedStringGenerator( "length <= ${lengthTrait.max.get()}" } + val constrainedTypeVisibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } + val constrainedTypeMetadata = RustMetadata( + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.Clone, RuntimeType.PartialEq, RuntimeType.Eq, RuntimeType.Hash)), + visibility = constrainedTypeVisibility + ) + // TODO Display impl does not honor `sensitive` trait. // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait writer.documentShape(shape, model, note = rustDocsNote(name)) + constrainedTypeMetadata.render(writer) writer.rustTemplate( """ - ##[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name(pub(crate) $inner); + struct $name(pub(crate) $inner); impl $name { /// ${rustDocsParseMethod(name, inner)} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 4dbc4113de..b0258ecd6c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -53,13 +53,14 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - Always implements either From for Structure or TryFrom for Structure. // - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. class ServerBuilderGenerator( - codegenContext: ServerCodegenContext, + private val codegenContext: ServerCodegenContext, private val shape: StructureShape, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider? = null, ) { private val takeInUnconstrainedTypes = pubCrateConstrainedShapeSymbolProvider != null private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) private val members: List = shape.allMembers.values.toList() @@ -221,6 +222,8 @@ class ServerBuilderGenerator( /** * Render a `foo` method to set shape member `foo`. The caller must provide a value with the exact same type * as the shape member's type. + * + * This method is meant for use by the user; it is not used by the generated crate's (de)serializers. */ private fun renderBuilderMemberFn( writer: RustWriter, @@ -233,6 +236,7 @@ class ServerBuilderGenerator( val wrapInMaybeConstrained = takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider) writer.documentShape(member, model) + if (hasBox && wrapInMaybeConstrained) { // In the case of recursive shapes, the member might be boxed. If so, and the member is also constrained, the // implementation of this function needs to immediately unbox the value to wrap it in `MaybeConstrained`, @@ -246,14 +250,20 @@ class ServerBuilderGenerator( rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { - val maybeConstrainedConstrained = "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + val maybeConstrainedVariant = if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) + // If constrained types are not public, the user is sending us an unconstrained type, and we + // will have to check the constraints when `build()` is called. + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" + else { + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + } // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { - rust("$maybeConstrainedConstrained($varExpr)") + rust("$maybeConstrainedVariant($varExpr)") } } } else { @@ -440,7 +450,7 @@ class ServerBuilderGenerator( private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val strippedOption = if (member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider)) { - symbolProvider.toSymbol(member) + constrainedShapeSymbolProvider.toSymbol(member) } else { pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 210f102c5b..4e13ef267e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -28,6 +28,7 @@ class UnconstrainedMapGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider: RustSymbolProvider, constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val unconstrainedModuleWriter: RustWriter, val shape: MapShape @@ -38,7 +39,7 @@ class UnconstrainedMapGenerator( private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) private val valueShape = model.expectShape(shape.value.target) private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { - symbolProvider.toSymbol(shape) + constrainedShapeSymbolProvider.toSymbol(shape) } else { pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index fe49d940f3..28614ffe2f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -24,7 +24,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.testutil.testSymbolProvider // These are the settings we default to if the user does not override them in their `smithy-build.json`. val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( @@ -43,7 +42,7 @@ fun serverTestSymbolProvider( model, serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ServerTestSymbolVisitorConfig, - publicConstrainedTypesEnabled = publicConstrainedTypesEnabled + publicConstrainedTypes = publicConstrainedTypesEnabled ) fun serverTestRustSettings( @@ -84,6 +83,7 @@ fun serverTestCodegenContext( ?: ServiceShape.builder().version("test").id("test#Service").build() val symbolProvider = serverTestSymbolProvider(model, serviceShape) val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + val constrainedShapeSymbolProvider = serverTestSymbolProvider(model, publicConstrainedTypesEnabled = true) val protocol = protocolShapeId ?: ShapeId.from("test#Protocol") return ServerCodegenContext( model, @@ -91,7 +91,8 @@ fun serverTestCodegenContext( service, protocol, settings, - unconstrainedShapeSymbolProvider + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, ) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt index 1b8ca834c6..819af80258 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt @@ -171,7 +171,9 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n val Debug = stdfmt.member("Debug") val Default: RuntimeType = RuntimeType("Default", dependency = null, namespace = "std::default") val Display = stdfmt.member("Display") + val Eq = std.member("cmp::Eq") val From = RuntimeType("From", dependency = null, namespace = "std::convert") + val Hash = std.member("hash::Hash") val TryFrom = RuntimeType("TryFrom", dependency = null, namespace = "std::convert") val PartialEq = std.member("cmp::PartialEq") val StdError = RuntimeType("Error", dependency = null, namespace = "std::error") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt index 67f286f84d..378c1a8b15 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt @@ -26,6 +26,7 @@ data class ServerCodegenContext( override val protocol: ShapeId, override val settings: ServerRustSettings, val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val constrainedShapeSymbolProvider: RustSymbolProvider, ) : CoreCodegenContext( model, symbolProvider, serviceShape, protocol, settings, CodegenTarget.SERVER, ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt index 8b74d6411d..60453d60c3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt @@ -70,22 +70,35 @@ data class ServerRustSettings( } } +/** + * [publicConstrainedTypes]: Generate constrained wrapper newtypes for constrained shapes + */ data class ServerCodegenConfig( override val formatTimeoutSeconds: Int = defaultFormatTimeoutSeconds, override val debugMode: Boolean = defaultDebugMode, override val eventStreamAllowList: Set = defaultEventStreamAllowList, + // TODO Unit test that we don't generate public constrained types when this setting is enabled. + val publicConstrainedTypes: Boolean = true ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, eventStreamAllowList, ) { companion object { - // Note `node` is unused, because at the moment `ServerCodegenConfig` has the same properties as - // `CodegenConfig`. In the future, the server will have server-specific codegen options just like the client - // does. + private const val defaultPublicConstrainedTypes = true + fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = - ServerCodegenConfig( - formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, - debugMode = coreCodegenConfig.debugMode, - eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, - ) + if (node.isPresent) { + ServerCodegenConfig( + formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, + debugMode = coreCodegenConfig.debugMode, + eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, + publicConstrainedTypes = node.get().getBooleanMemberOrDefault("publicConstrainedTypes", defaultPublicConstrainedTypes) + ) + } else { + ServerCodegenConfig( + formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, + debugMode = coreCodegenConfig.debugMode, + eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, + ) + } } } From fbba870f63a03ebb4d44d5e045c3528768276c16 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 11 Aug 2022 16:56:45 +0200 Subject: [PATCH 136/255] sst constraints_without_public_constrained_types,constraints - ConstrainedShapesOperation, partially commented; works --- codegen-server-test/build.gradle.kts | 3 +- codegen-server-test/model/constraints.smithy | 46 +++++++++---------- .../server/smithy/ServerCodegenVisitor.kt | 2 + .../generators/ConstrainedMapGenerator.kt | 38 ++++++++++++--- .../generators/ConstrainedStringGenerator.kt | 13 +++++- .../generators/ServerBuilderGenerator.kt | 37 +++++++++++++-- .../generators/UnconstrainedMapGenerator.kt | 27 +++++++++-- .../server/smithy/protocols/ServerAwsJson.kt | 12 ++--- .../ServerHttpBoundProtocolGenerator.kt | 2 +- ...erRestJsonFactory.kt => ServerRestJson.kt} | 18 +++++++- .../smithy/rust/codegen/rustlang/RustTypes.kt | 1 + .../smithy/rust/codegen/smithy/Constraints.kt | 3 +- .../rust/codegen/smithy/ServerRustSettings.kt | 6 +-- .../rust/codegen/smithy/protocols/RestJson.kt | 2 +- .../serialize/JsonSerializerGenerator.kt | 5 +- 15 files changed, 160 insertions(+), 55 deletions(-) rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/{ServerRestJsonFactory.kt => ServerRestJson.kt} (61%) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 717be94a67..0265d6ae50 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -35,9 +35,10 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), CodegenTest( - "com.amazonaws.simple#ConstraintsService", "constraints", + "com.amazonaws.simple#ConstraintsService", "constraints_without_public_constrained_types", extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, ), + CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index f0a900d5c9..af58f5eeb2 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -10,20 +10,20 @@ use aws.protocols#restJson1 service ConstraintsService { operations: [ ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfEnumStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - QueryParamsTargetingMapOfListOfEnumStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // HttpPrefixHeadersTargetingMapOfEnumStringOperation, +// ConstrainedHttpBoundShapesOperation, +// ConstrainedRecursiveShapesOperation, +// // `httpQueryParams` and `httpPrefixHeaders` are structurually +// // exclusive, so we need one operation per target shape type +// // combination. +// QueryParamsTargetingLengthMapOperation, +// QueryParamsTargetingMapOfLengthStringOperation, +// QueryParamsTargetingMapOfEnumStringOperation, +// QueryParamsTargetingMapOfListOfLengthStringOperation, +// QueryParamsTargetingMapOfSetOfLengthStringOperation, +// QueryParamsTargetingMapOfListOfEnumStringOperation, +// HttpPrefixHeadersTargetingLengthMapOperation, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) +// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -205,15 +205,15 @@ structure ConA { conBSet: ConBSet, conBMap: ConBMap, - - mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - - constrainedUnion: ConstrainedUnion, - enumString: EnumString, - - listOfLengthString: ListOfLengthString, - - setOfLengthString: SetOfLengthString, +// +// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, +// +// constrainedUnion: ConstrainedUnion, +// enumString: EnumString, +// +// listOfLengthString: ListOfLengthString, +// +// setOfLengthString: SetOfLengthString, } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index d4e9eac4c5..09e439509c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -301,6 +301,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, constraintViolationSymbolProvider, + codegenContext.settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, shape ).render() @@ -329,6 +330,7 @@ open class ServerCodegenVisitor( constrainedShapeSymbolProvider, constraintViolationSymbolProvider, settings.codegenConfig.publicConstrainedTypes, + symbolProvider, modelsModuleWriter, shape, if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 3264cdb739..64ad38dfa1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -37,6 +37,7 @@ class ConstrainedMapGenerator( private val constrainedSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val publicConstrainedTypes: Boolean, + private val symbolProvider: RustSymbolProvider, val writer: RustWriter, val shape: MapShape, private val unconstrainedSymbol: Symbol? = null, @@ -67,14 +68,23 @@ class ConstrainedMapGenerator( visibility = constrainedTypeVisibility ) + val codegenScope = arrayOf( + "KeySymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.key.target)), + "ValueSymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.value.target)), + "TryFrom" to RuntimeType.TryFrom, + "ConstraintViolation" to constraintViolation + ) + // TODO Display impl missing; it should honor `sensitive` trait. writer.documentShape(shape, model, note = rustDocsNote(name)) constrainedTypeMetadata.render(writer) + writer.rustTemplate("struct $name(pub(crate) $inner);", *codegenScope) + if (constrainedTypeVisibility == Visibility.PUBCRATE) { + Attribute.AllowUnused.render(writer) + } writer.rustTemplate( """ - struct $name(pub(crate) $inner); - impl $name { /// ${rustDocsParseMethod(name, inner)} pub fn parse(value: $inner) -> Result { @@ -105,10 +115,7 @@ class ConstrainedMapGenerator( } } """, - "KeySymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.key.target)), - "ValueSymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.value.target)), - "TryFrom" to RuntimeType.TryFrom, - "ConstraintViolation" to constraintViolation + *codegenScope ) if (unconstrainedSymbol != null) { @@ -122,5 +129,24 @@ class ConstrainedMapGenerator( "UnconstrainedSymbol" to unconstrainedSymbol, ) } + + if (!publicConstrainedTypes) { + // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces + // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). + writer.rustTemplate( + """ + impl #{From}<$name> for #{FullyUnconstrainedSymbol} { + fn from(constrained: $name) -> Self { + constrained.into_inner() + .into_iter() + .map(|(k, v)| (k.into(), v.into())) + .collect() + } + } + """, + "From" to RuntimeType.From, + "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index e802ce318e..8c82ad1107 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.render +import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType @@ -68,10 +69,12 @@ class ConstrainedStringGenerator( // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait writer.documentShape(shape, model, note = rustDocsNote(name)) constrainedTypeMetadata.render(writer) + writer.rust("struct $name(pub(crate) $inner);") + if (constrainedTypeVisibility == Visibility.PUBCRATE) { + Attribute.AllowUnused.render(writer) + } writer.rustTemplate( """ - struct $name(pub(crate) $inner); - impl $name { /// ${rustDocsParseMethod(name, inner)} pub fn parse(value: $inner) -> Result { @@ -124,6 +127,12 @@ class ConstrainedStringGenerator( } } } + + impl From<$name> for $inner { + fn from(value: $name) -> $inner { + value.into_inner() + } + } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index b0258ecd6c..25b61b0ef5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -32,8 +32,10 @@ import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed +import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.smithy.makeOptional @@ -67,6 +69,7 @@ class ServerBuilderGenerator( private val structureSymbol = symbolProvider.toSymbol(shape) private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), @@ -250,9 +253,11 @@ class ServerBuilderGenerator( rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { if (wrapInMaybeConstrained) { - val maybeConstrainedVariant = if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) - // If constrained types are not public, the user is sending us an unconstrained type, and we - // will have to check the constraints when `build()` is called. + val maybeConstrainedVariant = if (!publicConstrainedTypes && + member.hasPublicConstrainedWrapperTupleType(model)) + // If constrained types are not public and this member shape is one that would generate a + // public constrained type were the setting to be enabled, the user is sending us an unconstrained + // type, and we will have to check the constraints when `build()` is called. "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" else { "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" @@ -260,7 +265,19 @@ class ServerBuilderGenerator( // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" - if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" + if (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" + + // If constrained types are not public, the user is sending us a fully unconstrained type. + // If the shape is transitively but not directly constrained, we need to constrain the + // fully unconstrained inner types into their corresponding unconstrained types first. + val targetShape = model.expectShape(member.target) + if (!publicConstrainedTypes && + member.isTransitivelyConstrained(model, symbolProvider) && + !targetShape.isStructureShape && + !targetShape.isUnionShape) { + varExpr = "($varExpr).into()" + } + conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { rust("$maybeConstrainedVariant($varExpr)") @@ -533,6 +550,18 @@ class ServerBuilderGenerator( *codegenScope ) } + // If constrained types are not public and this is a member shape that would have generated a + // public constrained type, were the setting to be enabled, it means the user sent us an + // unconstrained type. We've just checked the constraints hold by going through the non-public + // constrained type, but the user wants to work with the unconstrained type, so we have to + // unwrap it. + if (!codegenContext.settings.codegenConfig.publicConstrainedTypes && + member.hasPublicConstrainedWrapperTupleType(model)) { + rust( + ".map(|v: #T| v.into())", + constrainedShapeSymbolProvider.toSymbol(model.expectShape(member.target)), + ) + } } builderMissingFieldForMember(member)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 4e13ef267e..ea52be3cfe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -28,8 +29,9 @@ class UnconstrainedMapGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider: RustSymbolProvider, + private val constrainedShapeSymbolProvider: RustSymbolProvider, constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, val shape: MapShape ) { @@ -62,7 +64,6 @@ class UnconstrainedMapGenerator( Self::Unconstrained(value) } } - """, "KeySymbol" to keySymbol, "ValueSymbol" to valueSymbol, @@ -70,6 +71,10 @@ class UnconstrainedMapGenerator( ) renderTryFromUnconstrainedForConstrained(this) + + if (!publicConstrainedTypes) { + renderFromFullyUnconstrainedForUnconstrained(this) + } } } @@ -86,7 +91,7 @@ class UnconstrainedMapGenerator( val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { - symbolProvider.toSymbol(valueShape) + constrainedShapeSymbolProvider.toSymbol(valueShape) } rustTemplate( @@ -153,4 +158,20 @@ class UnconstrainedMapGenerator( } } } + + private fun renderFromFullyUnconstrainedForUnconstrained(writer: RustWriter) { + // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces + // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). + writer.rustTemplate( + """ + impl #{From}<#{FullyUnconstrainedSymbol}> for $name { + fn from(fully_unconstrained: #{FullyUnconstrainedSymbol}) -> Self { + Self(fully_unconstrained.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + } + } + """, + "From" to RuntimeType.From, + "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), + ) + } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 6cd78583c1..582d9daa01 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable -import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJson @@ -82,22 +81,23 @@ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonCusto * https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#operation-error-serialization */ class ServerAwsJsonSerializerGenerator( - private val coreCodegenContext: CoreCodegenContext, + private val serverCodegenContext: ServerCodegenContext, private val httpBindingResolver: HttpBindingResolver, private val awsJsonVersion: AwsJsonVersion, private val jsonSerializerGenerator: JsonSerializerGenerator = JsonSerializerGenerator( - coreCodegenContext, + serverCodegenContext, httpBindingResolver, ::awsJsonFieldName, customizations = listOf(ServerAwsJsonError(awsJsonVersion)), + publicConstrainedTypes = serverCodegenContext.settings.codegenConfig.publicConstrainedTypes ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator class ServerAwsJson( - private val coreCodegenContext: CoreCodegenContext, + private val serverCodegenContext: ServerCodegenContext, private val awsJsonVersion: AwsJsonVersion, -) : AwsJson(coreCodegenContext, awsJsonVersion) { +) : AwsJson(serverCodegenContext, awsJsonVersion) { override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = - ServerAwsJsonSerializerGenerator(coreCodegenContext, httpBindingResolver, awsJsonVersion) + ServerAwsJsonSerializerGenerator(serverCodegenContext, httpBindingResolver, awsJsonVersion) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index f74ccfb683..a1d4e46199 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -80,7 +80,7 @@ import java.util.logging.Logger /* * Implement operations' input parsing and output serialization. Protocols can plug their own implementations * and overrides by creating a protocol factory inheriting from this class and feeding it to the [ServerProtocolLoader]. - * See `ServerRestJsonFactory.kt` for more info. + * See `ServerRestJson.kt` for more info. */ class ServerHttpBoundProtocolGenerator( codegenContext: ServerCodegenContext, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt similarity index 61% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index 193801fd71..d724f4e727 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJsonFactory.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,21 +5,25 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.smithy.protocols.RestJson +import software.amazon.smithy.rust.codegen.smithy.protocols.restJsonFieldName +import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.JsonSerializerGenerator +import software.amazon.smithy.rust.codegen.smithy.protocols.serialize.StructuredDataSerializerGenerator /** * RestJson1 server-side protocol factory. This factory creates the [ServerHttpProtocolGenerator] * with RestJson1 specific configurations. */ class ServerRestJsonFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ServerCodegenContext): Protocol = RestJson(codegenContext) + override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerRestJson(codegenContext) override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = - ServerHttpBoundProtocolGenerator(codegenContext, RestJson(codegenContext)) + ServerHttpBoundProtocolGenerator(codegenContext, ServerRestJson(codegenContext)) override fun support(): ProtocolSupport { return ProtocolSupport( @@ -36,3 +40,13 @@ class ServerRestJsonFactory : ProtocolGeneratorFactory false } +// TODO Make this method take in the `publicConstrainedTypes` boolean, even if it is just going to AND it with the rest of the condition, for API safety usage. fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (this) { is MapShape -> this.hasTrait() is StringShape -> !this.hasTrait() && this.hasTrait() @@ -75,4 +76,4 @@ fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) this.targetCanReachConstrainedShape(model, symbolProvider) } else { Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - } \ No newline at end of file + } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt index 60453d60c3..e58dd9af69 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt @@ -77,8 +77,8 @@ data class ServerCodegenConfig( override val formatTimeoutSeconds: Int = defaultFormatTimeoutSeconds, override val debugMode: Boolean = defaultDebugMode, override val eventStreamAllowList: Set = defaultEventStreamAllowList, - // TODO Unit test that we don't generate public constrained types when this setting is enabled. - val publicConstrainedTypes: Boolean = true + // TODO Unit test that we don't generate public constrained types when this setting is false. + val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, eventStreamAllowList, ) { @@ -91,7 +91,7 @@ data class ServerCodegenConfig( formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, debugMode = coreCodegenConfig.debugMode, eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, - publicConstrainedTypes = node.get().getBooleanMemberOrDefault("publicConstrainedTypes", defaultPublicConstrainedTypes) + publicConstrainedTypes = node.get().getBooleanMemberOrDefault("publicConstrainedTypes", defaultPublicConstrainedTypes), ) } else { ServerCodegenConfig( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt index 48e8a5bb7b..0c34ecb24c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/RestJson.kt @@ -87,7 +87,7 @@ class RestJsonHttpBindingResolver( } } -class RestJson(private val coreCodegenContext: CoreCodegenContext) : Protocol { +open class RestJson(private val coreCodegenContext: CoreCodegenContext) : Protocol { private val runtimeConfig = coreCodegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index 857276e910..42520dd68c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -72,6 +72,7 @@ class JsonSerializerGenerator( /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, private val customizations: List = listOf(), + private val publicConstrainedTypes: Boolean = false, ) : StructuredDataSerializerGenerator { private data class Context( /** Expression that retrieves a JsonValueWriter from either a JsonObjectWriter or JsonArrayWriter */ @@ -352,7 +353,7 @@ class JsonSerializerGenerator( val workingWithPublicConstrainedWrapperTupleType = codegenTarget == CodegenTarget.SERVER && context.shape.hasPublicConstrainedWrapperTupleType(model) - val value = if (workingWithPublicConstrainedWrapperTupleType) { + val value = if (publicConstrainedTypes && workingWithPublicConstrainedWrapperTupleType) { ValueExpression.Value("${context.valueExpression.name}.0") } else { context.valueExpression @@ -431,7 +432,7 @@ class JsonSerializerGenerator( val workingWithPublicConstrainedWrapperTupleType = codegenTarget == CodegenTarget.SERVER && keyTarget.hasPublicConstrainedWrapperTupleType(model) val keyExpression = if (workingWithPublicConstrainedWrapperTupleType) { - "$keyName.0.as_str()" + "$keyName.into_inner().as_str()" } else if (keyTarget.hasTrait()) { "$keyName.as_str()" } else { From 7cf173eee2a18e33f3b8f75fb33e0d841ef9b8d1 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 13:54:32 +0200 Subject: [PATCH 137/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA->ConB uncommented --- codegen-server-test/model/constraints.smithy | 26 ++++++------ .../server/smithy/ServerCodegenVisitor.kt | 3 +- .../generators/ServerBuilderGenerator.kt | 40 +++++++++++-------- .../UnconstrainedCollectionGenerator.kt | 26 +++++++++++- 4 files changed, 62 insertions(+), 33 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index af58f5eeb2..310684a59f 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -192,19 +192,19 @@ structure ConA { @required conB: ConB, - optConB: ConB, - - lengthString: LengthString, - minLengthString: MinLengthString, - maxLengthString: MaxLengthString, - fixedLengthString: FixedLengthString, - - conBList: ConBList, - conBList2: ConBList2, - - conBSet: ConBSet, - - conBMap: ConBMap, +// optConB: ConB, +// +// lengthString: LengthString, +// minLengthString: MinLengthString, +// maxLengthString: MaxLengthString, +// fixedLengthString: FixedLengthString, +// +// conBList: ConBList, +// conBList2: ConBList2, +// +// conBSet: ConBSet, +// +// conBMap: ConBMap, // // mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, // diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 09e439509c..f517207e3a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -264,6 +264,7 @@ open class ServerCodegenVisitor( unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, + settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, modelsModuleWriter, shape @@ -301,7 +302,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, constraintViolationSymbolProvider, - codegenContext.settings.codegenConfig.publicConstrainedTypes, + settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, shape ).render() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 25b61b0ef5..88a16b3cf4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -252,32 +252,38 @@ class ServerBuilderGenerator( writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { + val targetShape = model.expectShape(member.target) + // If constrained types are not public, the user is sending us a fully unconstrained type. + // If the shape is transitively but not directly constrained, we need to: + // 1. constrain the fully unconstrained inner types into their corresponding unconstrained types first; and + // 2. store the corresponding unconstrained type in a `MaybeConstrained::Unconstrained` variant. + // We will then check the constraints when `build()` is called. + // This condition is calculated once here and used later when the above two steps are performed. + // Note that we explicitly opt out of members targeting structure and union shapes: these shapes _always_ + // generate public constrained types, even when `publicConstrainedTypes` is disabled. + val isInputFullyUnconstrained = !publicConstrainedTypes && + member.isTransitivelyConstrained(model, symbolProvider) && + !targetShape.isStructureShape && + !targetShape.isUnionShape + if (wrapInMaybeConstrained) { - val maybeConstrainedVariant = if (!publicConstrainedTypes && - member.hasPublicConstrainedWrapperTupleType(model)) - // If constrained types are not public and this member shape is one that would generate a - // public constrained type were the setting to be enabled, the user is sending us an unconstrained - // type, and we will have to check the constraints when `build()` is called. - "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" - else { - "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - } // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" if (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" - // If constrained types are not public, the user is sending us a fully unconstrained type. - // If the shape is transitively but not directly constrained, we need to constrain the - // fully unconstrained inner types into their corresponding unconstrained types first. - val targetShape = model.expectShape(member.target) - if (!publicConstrainedTypes && - member.isTransitivelyConstrained(model, symbolProvider) && - !targetShape.isStructureShape && - !targetShape.isUnionShape) { + if (isInputFullyUnconstrained) { + // Step 1 above. varExpr = "($varExpr).into()" } + val maybeConstrainedVariant = if (isInputFullyUnconstrained) + // Step 2 above. + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" + else { + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + } + conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { rust("$maybeConstrainedVariant($varExpr)") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 1752099769..6445a96109 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -26,16 +26,18 @@ class UnconstrainedCollectionGenerator( private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: CollectionShape ) { + private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) + private val name = symbol.name + fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() - val name = symbol.name val innerShape = model.expectShape(shape.member.target) val innerUnconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) @@ -76,6 +78,10 @@ class UnconstrainedCollectionGenerator( "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "TryFrom" to RuntimeType.TryFrom, ) + +// if (!publicConstrainedTypes) { +// renderFromFullyUnconstrainedForUnconstrained(this) +// } } modelsModuleWriter.withModule( @@ -90,4 +96,20 @@ class UnconstrainedCollectionGenerator( ) } } + + private fun renderFromFullyUnconstrainedForUnconstrained(writer: RustWriter) { + // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces + // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). + writer.rustTemplate( + """ + impl #{From}<#{FullyUnconstrainedSymbol}> for $name { + fn from(fully_unconstrained: #{FullyUnconstrainedSymbol}) -> Self { + Self(fully_unconstrained.into_iter().map(|v| v.into()).collect()) + } + } + """, + "From" to RuntimeType.From, + "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), + ) + } } From 850a1bf635cc454f1c1a1c0eaad69898776dfa80 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 13:57:17 +0200 Subject: [PATCH 138/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until FixedLengthString uncommented --- codegen-server-test/model/constraints.smithy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 310684a59f..587d376966 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -192,12 +192,12 @@ structure ConA { @required conB: ConB, -// optConB: ConB, -// -// lengthString: LengthString, -// minLengthString: MinLengthString, -// maxLengthString: MaxLengthString, -// fixedLengthString: FixedLengthString, + optConB: ConB, + + lengthString: LengthString, + minLengthString: MinLengthString, + maxLengthString: MaxLengthString, + fixedLengthString: FixedLengthString, // // conBList: ConBList, // conBList2: ConBList2, From 194a19b37f28052ed3dad5630404f35c35601d8b Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 14:41:51 +0200 Subject: [PATCH 139/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until conBSet uncommented --- codegen-server-test/model/constraints.smithy | 10 +++---- .../generators/ServerBuilderGenerator.kt | 28 +++++++++---------- .../UnconstrainedCollectionGenerator.kt | 2 ++ .../generators/UnconstrainedMapGenerator.kt | 8 ++++-- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 587d376966..0f67fe5dcd 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -198,11 +198,11 @@ structure ConA { minLengthString: MinLengthString, maxLengthString: MaxLengthString, fixedLengthString: FixedLengthString, -// -// conBList: ConBList, -// conBList2: ConBList2, -// -// conBSet: ConBSet, + + conBList: ConBList, + conBList2: ConBList2, + + conBSet: ConBSet, // // conBMap: ConBMap, // diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 88a16b3cf4..d2b9a67295 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -33,9 +33,9 @@ import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed -import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.smithy.makeOptional @@ -253,27 +253,25 @@ class ServerBuilderGenerator( rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { val targetShape = model.expectShape(member.target) - // If constrained types are not public, the user is sending us a fully unconstrained type. - // If the shape is transitively but not directly constrained, we need to: - // 1. constrain the fully unconstrained inner types into their corresponding unconstrained types first; and - // 2. store the corresponding unconstrained type in a `MaybeConstrained::Unconstrained` variant. - // We will then check the constraints when `build()` is called. + // If constrained types are not public and the target shape is one that would generate a public constrained + // type had the `publicConstrainedTypes` setting been enabled, then we need to: + // 1. constrain the input type into the corresponding `pub(crate)` unconstrained types first; and + // 2. store the resulting value in a `MaybeConstrained::Unconstrained` variant. // This condition is calculated once here and used later when the above two steps are performed. - // Note that we explicitly opt out of members targeting structure and union shapes: these shapes _always_ - // generate public constrained types, even when `publicConstrainedTypes` is disabled. + // Note we explicitly opt out when the target shape is a structure shape, since in that case public + // constrained types are _always_ generated, regardless of whether `publicConstrainedTypes` is enabled, + // and structure shapes are directly constrained when at least one of their members is non-optional. val isInputFullyUnconstrained = !publicConstrainedTypes && - member.isTransitivelyConstrained(model, symbolProvider) && - !targetShape.isStructureShape && - !targetShape.isUnionShape + targetShape.isDirectlyConstrained(symbolProvider) && + !targetShape.isStructureShape if (wrapInMaybeConstrained) { // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" - if (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" - - if (isInputFullyUnconstrained) { - // Step 1 above. + // TODO I think the first part of the condition could be refined, and we could get rid of the + // `redundant_closure` allow lint below. + if (!publicConstrainedTypes || (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member))) { varExpr = "($varExpr).into()" } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 6445a96109..661236db28 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -79,6 +79,8 @@ class UnconstrainedCollectionGenerator( "TryFrom" to RuntimeType.TryFrom, ) + // TODO I thought I needed this converter but turns out I don't. Remove it if it ends up I truly don't end + // up needing it. // if (!publicConstrainedTypes) { // renderFromFullyUnconstrainedForUnconstrained(this) // } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index ea52be3cfe..b547bece25 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -72,9 +72,11 @@ class UnconstrainedMapGenerator( renderTryFromUnconstrainedForConstrained(this) - if (!publicConstrainedTypes) { - renderFromFullyUnconstrainedForUnconstrained(this) - } + // TODO I thought I needed this converter but turns out I don't. Remove it if it ends up I truly don't end + // up needing it. +// if (!publicConstrainedTypes) { +// renderFromFullyUnconstrainedForUnconstrained(this) +// } } } From 7a6b9ca4777d6d0dfa69c4fa60b0b2f90bf58673 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 14:55:56 +0200 Subject: [PATCH 140/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until conBMap uncommented --- codegen-server-test/model/constraints.smithy | 6 +++--- .../server/smithy/generators/ServerBuilderGenerator.kt | 2 -- .../server/smithy/generators/UnconstrainedMapGenerator.kt | 8 +++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 0f67fe5dcd..6e54199c40 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -203,9 +203,9 @@ structure ConA { conBList2: ConBList2, conBSet: ConBSet, -// -// conBMap: ConBMap, -// + + conBMap: ConBMap, + // mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, // // constrainedUnion: ConstrainedUnion, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index d2b9a67295..d0b84a4c57 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -269,8 +269,6 @@ class ServerBuilderGenerator( // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" - // TODO I think the first part of the condition could be refined, and we could get rid of the - // `redundant_closure` allow lint below. if (!publicConstrainedTypes || (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member))) { varExpr = "($varExpr).into()" } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index b547bece25..ea52be3cfe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -72,11 +72,9 @@ class UnconstrainedMapGenerator( renderTryFromUnconstrainedForConstrained(this) - // TODO I thought I needed this converter but turns out I don't. Remove it if it ends up I truly don't end - // up needing it. -// if (!publicConstrainedTypes) { -// renderFromFullyUnconstrainedForUnconstrained(this) -// } + if (!publicConstrainedTypes) { + renderFromFullyUnconstrainedForUnconstrained(this) + } } } From ce42e3e290636eaa3da1569709976a0cffdccbb9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 15:04:18 +0200 Subject: [PATCH 141/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until mapOfMapOfListOfListOfConB uncommented --- codegen-server-test/model/constraints.smithy | 2 +- .../server/smithy/generators/UnconstrainedMapGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 6e54199c40..db22c1f34a 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -206,7 +206,7 @@ structure ConA { conBMap: ConBMap, -// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, + mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, // // constrainedUnion: ConstrainedUnion, // enumString: EnumString, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index ea52be3cfe..6da3f128f0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -72,7 +72,7 @@ class UnconstrainedMapGenerator( renderTryFromUnconstrainedForConstrained(this) - if (!publicConstrainedTypes) { + if (!publicConstrainedTypes && shape.isDirectlyConstrained(symbolProvider)) { renderFromFullyUnconstrainedForUnconstrained(this) } } From 9f8f925825662652846db27495b854e59b1c7348 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 15:36:06 +0200 Subject: [PATCH 142/255] small refactor in UnconstrainedUnionGenerator --- codegen-server-test/model/constraints.smithy | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 11 ++++++----- .../generators/UnconstrainedUnionGenerator.kt | 16 ++++++++-------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index db22c1f34a..a9812d22b7 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -207,7 +207,7 @@ structure ConA { conBMap: ConBMap, mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, -// + // constrainedUnion: ConstrainedUnion, // enumString: EnumString, // diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index f517207e3a..1021a73058 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -381,11 +381,11 @@ open class ServerCodegenVisitor( if (shape.hasTrait() && shape.isDirectlyConstrained(symbolProvider)) { logger.warning( """ - String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy - spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the - constraints (which it currently does not), not each code generator. - See https://github.com/awslabs/smithy/issues/1121f for more information. - """.trimIndent() + String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy + spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the + constraints (which it currently does not), not each code generator. + See https://github.com/awslabs/smithy/issues/1121f for more information. + """.trimIndent() ) } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") @@ -428,6 +428,7 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, constraintViolationSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 1c7d21add5..1e6cae5ac8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -39,6 +39,7 @@ class UnconstrainedUnionGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, + private val constrainedShapeSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, @@ -167,22 +168,21 @@ class UnconstrainedUnionGenerator( if (resolveToNonPublicConstrainedType) { rustTemplate( """ - let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained - .try_into() + #{PubCrateConstrainedShapeSymbol}::try_from(unconstrained) ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } - .map_err(Self::Error::${ConstraintViolation(member).name()})?; - constrained.into() + .map_err(Self::Error::${ConstraintViolation(member).name()})? + .into() """, "PubCrateConstrainedShapeSymbol" to pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) ) } else { - rust( + rustTemplate( """ - unconstrained - .try_into() + #{ConstrainedShapeSymbol}::try_from(unconstrained) ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } .map_err(Self::Error::${ConstraintViolation(member).name()})? - """ + """, + "ConstrainedShapeSymbol" to constrainedShapeSymbolProvider.toSymbol(targetShape) ) } } else { From 035e4c7723bfb7e2c54784d7438db40122edc902 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 16:33:34 +0200 Subject: [PATCH 143/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until constrainedUnion uncommented --- codegen-server-test/model/constraints.smithy | 2 +- .../rust/codegen/server/smithy/ServerCodegenVisitor.kt | 1 + .../server/smithy/generators/UnconstrainedUnionGenerator.kt | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index a9812d22b7..1395f17938 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -208,7 +208,7 @@ structure ConA { mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, -// constrainedUnion: ConstrainedUnion, + constrainedUnion: ConstrainedUnion, // enumString: EnumString, // // listOfLengthString: ListOfLengthString, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 1021a73058..7a481837ed 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -430,6 +430,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, constraintViolationSymbolProvider, + settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, modelsModuleWriter, shape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 1e6cae5ac8..3b939f5b48 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -41,6 +41,7 @@ class UnconstrainedUnionGenerator( private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constrainedShapeSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: UnionShape @@ -184,6 +185,10 @@ class UnconstrainedUnionGenerator( """, "ConstrainedShapeSymbol" to constrainedShapeSymbolProvider.toSymbol(targetShape) ) + + if (!publicConstrainedTypes) { + rust(".into()") + } } } else { rust("unconstrained") From bbfc42e2a0080fed8ac59fcdd494f6e677dc9761 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 16:46:15 +0200 Subject: [PATCH 144/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA-> until enumString uncommented --- codegen-server-test/model/constraints.smithy | 2 +- .../smithy/generators/ServerBuilderGenerator.kt | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 1395f17938..a1a10fc1c1 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -209,7 +209,7 @@ structure ConA { mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, constrainedUnion: ConstrainedUnion, -// enumString: EnumString, + enumString: EnumString, // // listOfLengthString: ListOfLengthString, // diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index d0b84a4c57..987499a493 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -258,12 +259,14 @@ class ServerBuilderGenerator( // 1. constrain the input type into the corresponding `pub(crate)` unconstrained types first; and // 2. store the resulting value in a `MaybeConstrained::Unconstrained` variant. // This condition is calculated once here and used later when the above two steps are performed. - // Note we explicitly opt out when the target shape is a structure shape, since in that case public - // constrained types are _always_ generated, regardless of whether `publicConstrainedTypes` is enabled, - // and structure shapes are directly constrained when at least one of their members is non-optional. + // Note we explicitly opt out when the target shape is a structure shape or a string shape with the + // `enum` trait, since in those cases public constrained types are _always_ generated, regardless of + // whether `publicConstrainedTypes` is enabled (remember structure shapes are directly constrained + // when at least one of their members is non-optional). val isInputFullyUnconstrained = !publicConstrainedTypes && targetShape.isDirectlyConstrained(symbolProvider) && - !targetShape.isStructureShape + !targetShape.isStructureShape && + !(targetShape.isStringShape && targetShape.hasTrait()) if (wrapInMaybeConstrained) { // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). From 91cce7a84f86a30da6d5855088e12d0f79bf903c Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 22 Aug 2022 18:49:29 +0200 Subject: [PATCH 145/255] save work --- codegen-server-test/model/constraints.smithy | 8 ++-- .../server/smithy/ServerCodegenVisitor.kt | 2 + .../PubCrateConstrainedCollectionGenerator.kt | 37 ++++++++++++++----- .../PubCrateConstrainedMapGenerator.kt | 1 + .../generators/ServerBuilderGenerator.kt | 5 +-- .../UnconstrainedCollectionGenerator.kt | 10 ++--- .../smithy/rust/codegen/smithy/Constraints.kt | 7 ++++ 7 files changed, 49 insertions(+), 21 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index a1a10fc1c1..fb9dd97b76 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -210,10 +210,10 @@ structure ConA { constrainedUnion: ConstrainedUnion, enumString: EnumString, -// -// listOfLengthString: ListOfLengthString, -// -// setOfLengthString: SetOfLengthString, + + listOfLengthString: ListOfLengthString, + +// setOfLengthString: SetOfLengthString, } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 7a481837ed..7a655dbced 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -279,6 +279,8 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, + settings.codegenConfig.publicConstrainedTypes, writer, shape ).render() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index b3205449a9..fa54fd4fa3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -40,22 +40,24 @@ class PubCrateConstrainedCollectionGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, + private val constrainedShapeSymbolProvider: RustSymbolProvider, + private val publicConstrainedTypes: Boolean, val writer: RustWriter, val shape: CollectionShape ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val symbol = symbolProvider.toSymbol(shape) - val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) + val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) + val pubCrateConstrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) - val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() - val name = constrainedSymbol.name + val module = pubCrateConstrainedSymbol.namespace.split(pubCrateConstrainedSymbol.namespaceDelimiter).last() + val name = pubCrateConstrainedSymbol.name val innerShape = model.expectShape(shape.member.target) val innerConstrainedSymbol = if (innerShape.isTransitivelyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) } else { - symbolProvider.toSymbol(innerShape) + constrainedShapeSymbolProvider.toSymbol(innerShape) } // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, @@ -83,8 +85,8 @@ class PubCrateConstrainedCollectionGenerator( type Unconstrained = #{UnconstrainedSymbol}; } - impl From<#{Symbol}> for $name { - fn from(v: #{Symbol}) -> Self { + impl #{From}<#{ConstrainedSymbol}> for $name { + fn from(v: #{ConstrainedSymbol}) -> Self { ${ if (innerNeedsConstraining) { "Self(v.into_iter().map(|item| item.into()).collect())" } else { @@ -93,7 +95,7 @@ class PubCrateConstrainedCollectionGenerator( } } - impl From<$name> for #{Symbol} { + impl #{From}<$name> for #{ConstrainedSymbol} { fn from(v: $name) -> Self { ${ if (innerNeedsConstraining) { "v.0.into_iter().map(|item| item.into()).collect()" @@ -103,11 +105,28 @@ class PubCrateConstrainedCollectionGenerator( } } """, + "From" to RuntimeType.From, "InnerConstrainedSymbol" to innerConstrainedSymbol, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "UnconstrainedSymbol" to unconstrainedSymbol, - "Symbol" to symbol, + "ConstrainedSymbol" to constrainedSymbol, ) + +// if (!publicConstrainedTypes) { +// // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces +// // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). +// rustTemplate( +// """ +// impl #{From}<$name> for #{FullyUnconstrainedSymbol} { +// fn from(v: $name) -> Self { +// v.0.into_iter().map(|item| item.into()).collect() +// } +// } +// """, +// "From" to RuntimeType.From, +// "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), +// ) +// } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 564aa1153f..af8ee341f0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -57,6 +57,7 @@ class PubCrateConstrainedMapGenerator( val valueSymbol = if (valueShape.isTransitivelyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { + // TODO Should use constrainedShapeSymbolProvider, see PubCrateConstrainedCollectionGenerator symbolProvider.toSymbol(valueShape) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 987499a493..391fd2fbc2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -29,12 +29,12 @@ import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbol import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShapeOtherThanConstrainedStructureShape import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType -import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -264,8 +264,7 @@ class ServerBuilderGenerator( // whether `publicConstrainedTypes` is enabled (remember structure shapes are directly constrained // when at least one of their members is non-optional). val isInputFullyUnconstrained = !publicConstrainedTypes && - targetShape.isDirectlyConstrained(symbolProvider) && - !targetShape.isStructureShape && + targetShape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model, symbolProvider) !(targetShape.isStringShape && targetShape.hasTrait()) if (wrapInMaybeConstrained) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 661236db28..ccb6fb57ba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShapeOtherThanConstrainedStructureShape import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs @@ -79,11 +80,10 @@ class UnconstrainedCollectionGenerator( "TryFrom" to RuntimeType.TryFrom, ) - // TODO I thought I needed this converter but turns out I don't. Remove it if it ends up I truly don't end - // up needing it. -// if (!publicConstrainedTypes) { -// renderFromFullyUnconstrainedForUnconstrained(this) -// } + // TODO I'm sure we'll have to eventually add the converter from structure shape to structure shape builder and remove the second condition. + if (!publicConstrainedTypes && !shape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model, symbolProvider)) { + renderFromFullyUnconstrainedForUnconstrained(this) + } } modelsModuleWriter.withModule( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index be200765da..4b1bbb3324 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -77,3 +77,10 @@ fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) } else { Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } } + +fun Shape.canReachStructureShape(model: Model) = Walker(model).walkShapes(this).toSet().any { it.isStructureShape } + +fun Shape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model: Model, symbolProvider: SymbolProvider) = + Walker(model).walkShapes(this).toSet() + .filter { it.isDirectlyConstrained(symbolProvider) } + .any { !it.isStructureShape } From a674f9ae6bd31871e7942946e8b1a97542ea88f8 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 23 Aug 2022 13:48:41 +0200 Subject: [PATCH 146/255] Revert more or less until 4f0ffefb, with constraints.smithy working fine --- codegen-server-test/model/constraints.smithy | 30 +++++------ .../server/smithy/ServerCodegenVisitor.kt | 12 +---- .../generators/ConstrainedMapGenerator.kt | 20 -------- .../generators/ConstrainedStringGenerator.kt | 6 --- .../PubCrateConstrainedCollectionGenerator.kt | 37 ++++---------- .../PubCrateConstrainedMapGenerator.kt | 1 - .../generators/ServerBuilderGenerator.kt | 51 ++----------------- .../UnconstrainedCollectionGenerator.kt | 28 +--------- .../generators/UnconstrainedMapGenerator.kt | 28 ++-------- .../generators/UnconstrainedUnionGenerator.kt | 21 +++----- .../server/smithy/protocols/ServerAwsJson.kt | 1 + .../smithy/rust/codegen/smithy/Constraints.kt | 9 +--- .../serialize/JsonSerializerGenerator.kt | 2 +- 13 files changed, 47 insertions(+), 199 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index fb9dd97b76..f0a900d5c9 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -10,20 +10,20 @@ use aws.protocols#restJson1 service ConstraintsService { operations: [ ConstrainedShapesOperation, -// ConstrainedHttpBoundShapesOperation, -// ConstrainedRecursiveShapesOperation, -// // `httpQueryParams` and `httpPrefixHeaders` are structurually -// // exclusive, so we need one operation per target shape type -// // combination. -// QueryParamsTargetingLengthMapOperation, -// QueryParamsTargetingMapOfLengthStringOperation, -// QueryParamsTargetingMapOfEnumStringOperation, -// QueryParamsTargetingMapOfListOfLengthStringOperation, -// QueryParamsTargetingMapOfSetOfLengthStringOperation, -// QueryParamsTargetingMapOfListOfEnumStringOperation, -// HttpPrefixHeadersTargetingLengthMapOperation, -// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) -// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + ConstrainedHttpBoundShapesOperation, + ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfEnumStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, + QueryParamsTargetingMapOfListOfEnumStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + // HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -213,7 +213,7 @@ structure ConA { listOfLengthString: ListOfLengthString, -// setOfLengthString: SetOfLengthString, + setOfLengthString: SetOfLengthString, } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 7a655dbced..7e16fe5711 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -78,9 +78,9 @@ open class ServerCodegenVisitor( protected val settings = ServerRustSettings.from(context.model, context.settings) protected var symbolProvider: RustSymbolProvider - protected var constrainedShapeSymbolProvider: RustSymbolProvider protected val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider + private val constrainedShapeSymbolProvider: RustSymbolProvider protected val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider protected var rustCrate: RustCrate private val fileManifest = context.fileManifest @@ -264,7 +264,6 @@ open class ServerCodegenVisitor( unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, modelsModuleWriter, shape @@ -279,8 +278,6 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, writer, shape ).render() @@ -302,9 +299,7 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, constraintViolationSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, shape ).render() @@ -333,7 +328,6 @@ open class ServerCodegenVisitor( constrainedShapeSymbolProvider, constraintViolationSymbolProvider, settings.codegenConfig.publicConstrainedTypes, - symbolProvider, modelsModuleWriter, shape, if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null @@ -394,7 +388,7 @@ open class ServerCodegenVisitor( rustCrate.withModule(ModelsModule) { writer -> ConstrainedStringGenerator( model, - constrainedShapeSymbolProvider, + symbolProvider, constraintViolationSymbolProvider, settings.codegenConfig.publicConstrainedTypes, writer, @@ -430,9 +424,7 @@ open class ServerCodegenVisitor( symbolProvider, unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, constraintViolationSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, unconstrainedModuleWriter, modelsModuleWriter, shape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 64ad38dfa1..e169a75326 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -37,7 +37,6 @@ class ConstrainedMapGenerator( private val constrainedSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val publicConstrainedTypes: Boolean, - private val symbolProvider: RustSymbolProvider, val writer: RustWriter, val shape: MapShape, private val unconstrainedSymbol: Symbol? = null, @@ -129,24 +128,5 @@ class ConstrainedMapGenerator( "UnconstrainedSymbol" to unconstrainedSymbol, ) } - - if (!publicConstrainedTypes) { - // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces - // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). - writer.rustTemplate( - """ - impl #{From}<$name> for #{FullyUnconstrainedSymbol} { - fn from(constrained: $name) -> Self { - constrained.into_inner() - .into_iter() - .map(|(k, v)| (k.into(), v.into())) - .collect() - } - } - """, - "From" to RuntimeType.From, - "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), - ) - } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 8c82ad1107..11716693d0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -127,12 +127,6 @@ class ConstrainedStringGenerator( } } } - - impl From<$name> for $inner { - fn from(value: $name) -> $inner { - value.into_inner() - } - } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index fa54fd4fa3..b3205449a9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -40,24 +40,22 @@ class PubCrateConstrainedCollectionGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: RustSymbolProvider, - private val publicConstrainedTypes: Boolean, val writer: RustWriter, val shape: CollectionShape ) { fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) - val constrainedSymbol = constrainedShapeSymbolProvider.toSymbol(shape) - val pubCrateConstrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) + val symbol = symbolProvider.toSymbol(shape) + val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) - val module = pubCrateConstrainedSymbol.namespace.split(pubCrateConstrainedSymbol.namespaceDelimiter).last() - val name = pubCrateConstrainedSymbol.name + val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() + val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) val innerConstrainedSymbol = if (innerShape.isTransitivelyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) } else { - constrainedShapeSymbolProvider.toSymbol(innerShape) + symbolProvider.toSymbol(innerShape) } // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, @@ -85,8 +83,8 @@ class PubCrateConstrainedCollectionGenerator( type Unconstrained = #{UnconstrainedSymbol}; } - impl #{From}<#{ConstrainedSymbol}> for $name { - fn from(v: #{ConstrainedSymbol}) -> Self { + impl From<#{Symbol}> for $name { + fn from(v: #{Symbol}) -> Self { ${ if (innerNeedsConstraining) { "Self(v.into_iter().map(|item| item.into()).collect())" } else { @@ -95,7 +93,7 @@ class PubCrateConstrainedCollectionGenerator( } } - impl #{From}<$name> for #{ConstrainedSymbol} { + impl From<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ if (innerNeedsConstraining) { "v.0.into_iter().map(|item| item.into()).collect()" @@ -105,28 +103,11 @@ class PubCrateConstrainedCollectionGenerator( } } """, - "From" to RuntimeType.From, "InnerConstrainedSymbol" to innerConstrainedSymbol, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "UnconstrainedSymbol" to unconstrainedSymbol, - "ConstrainedSymbol" to constrainedSymbol, + "Symbol" to symbol, ) - -// if (!publicConstrainedTypes) { -// // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces -// // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). -// rustTemplate( -// """ -// impl #{From}<$name> for #{FullyUnconstrainedSymbol} { -// fn from(v: $name) -> Self { -// v.0.into_iter().map(|item| item.into()).collect() -// } -// } -// """, -// "From" to RuntimeType.From, -// "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), -// ) -// } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index af8ee341f0..564aa1153f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -57,7 +57,6 @@ class PubCrateConstrainedMapGenerator( val valueSymbol = if (valueShape.isTransitivelyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { - // TODO Should use constrainedShapeSymbolProvider, see PubCrateConstrainedCollectionGenerator symbolProvider.toSymbol(valueShape) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 391fd2fbc2..07853c96c7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -29,12 +28,10 @@ import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbol import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShapeOtherThanConstrainedStructureShape import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -56,21 +53,19 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - Always implements either From for Structure or TryFrom for Structure. // - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. class ServerBuilderGenerator( - private val codegenContext: ServerCodegenContext, + codegenContext: ServerCodegenContext, private val shape: StructureShape, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider? = null, ) { private val takeInUnconstrainedTypes = pubCrateConstrainedShapeSymbolProvider != null private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider - private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) - private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), @@ -240,7 +235,6 @@ class ServerBuilderGenerator( val wrapInMaybeConstrained = takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider) writer.documentShape(member, model) - if (hasBox && wrapInMaybeConstrained) { // In the case of recursive shapes, the member might be boxed. If so, and the member is also constrained, the // implementation of this function needs to immediately unbox the value to wrap it in `MaybeConstrained`, @@ -253,38 +247,15 @@ class ServerBuilderGenerator( writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { rust("self.$memberName = ") conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - val targetShape = model.expectShape(member.target) - // If constrained types are not public and the target shape is one that would generate a public constrained - // type had the `publicConstrainedTypes` setting been enabled, then we need to: - // 1. constrain the input type into the corresponding `pub(crate)` unconstrained types first; and - // 2. store the resulting value in a `MaybeConstrained::Unconstrained` variant. - // This condition is calculated once here and used later when the above two steps are performed. - // Note we explicitly opt out when the target shape is a structure shape or a string shape with the - // `enum` trait, since in those cases public constrained types are _always_ generated, regardless of - // whether `publicConstrainedTypes` is enabled (remember structure shapes are directly constrained - // when at least one of their members is non-optional). - val isInputFullyUnconstrained = !publicConstrainedTypes && - targetShape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model, symbolProvider) - !(targetShape.isStringShape && targetShape.hasTrait()) - if (wrapInMaybeConstrained) { + val maybeConstrainedConstrained = "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" - if (!publicConstrainedTypes || (publicConstrainedTypes && !constrainedTypeHoldsFinalType(member))) { - varExpr = "($varExpr).into()" - } - - val maybeConstrainedVariant = if (isInputFullyUnconstrained) - // Step 2 above. - "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" - else { - "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - } - + if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { - rust("$maybeConstrainedVariant($varExpr)") + rust("$maybeConstrainedConstrained($varExpr)") } } } else { @@ -471,7 +442,7 @@ class ServerBuilderGenerator( private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val strippedOption = if (member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider)) { - constrainedShapeSymbolProvider.toSymbol(member) + symbolProvider.toSymbol(member) } else { pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } @@ -554,18 +525,6 @@ class ServerBuilderGenerator( *codegenScope ) } - // If constrained types are not public and this is a member shape that would have generated a - // public constrained type, were the setting to be enabled, it means the user sent us an - // unconstrained type. We've just checked the constraints hold by going through the non-public - // constrained type, but the user wants to work with the unconstrained type, so we have to - // unwrap it. - if (!codegenContext.settings.codegenConfig.publicConstrainedTypes && - member.hasPublicConstrainedWrapperTupleType(model)) { - rust( - ".map(|v: #T| v.into())", - constrainedShapeSymbolProvider.toSymbol(model.expectShape(member.target)), - ) - } } builderMissingFieldForMember(member)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index ccb6fb57ba..1752099769 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShapeOtherThanConstrainedStructureShape import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs @@ -27,18 +26,16 @@ class UnconstrainedCollectionGenerator( private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: CollectionShape ) { - private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) - private val name = symbol.name - fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) + val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = symbol.namespace.split(symbol.namespaceDelimiter).last() + val name = symbol.name val innerShape = model.expectShape(shape.member.target) val innerUnconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(innerShape) val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) @@ -79,11 +76,6 @@ class UnconstrainedCollectionGenerator( "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "TryFrom" to RuntimeType.TryFrom, ) - - // TODO I'm sure we'll have to eventually add the converter from structure shape to structure shape builder and remove the second condition. - if (!publicConstrainedTypes && !shape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model, symbolProvider)) { - renderFromFullyUnconstrainedForUnconstrained(this) - } } modelsModuleWriter.withModule( @@ -98,20 +90,4 @@ class UnconstrainedCollectionGenerator( ) } } - - private fun renderFromFullyUnconstrainedForUnconstrained(writer: RustWriter) { - // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces - // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). - writer.rustTemplate( - """ - impl #{From}<#{FullyUnconstrainedSymbol}> for $name { - fn from(fully_unconstrained: #{FullyUnconstrainedSymbol}) -> Self { - Self(fully_unconstrained.into_iter().map(|v| v.into()).collect()) - } - } - """, - "From" to RuntimeType.From, - "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), - ) - } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 6da3f128f0..210f102c5b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -16,7 +16,6 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -29,9 +28,7 @@ class UnconstrainedMapGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: RustSymbolProvider, constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, val shape: MapShape ) { @@ -41,7 +38,7 @@ class UnconstrainedMapGenerator( private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) private val valueShape = model.expectShape(shape.value.target) private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { - constrainedShapeSymbolProvider.toSymbol(shape) + symbolProvider.toSymbol(shape) } else { pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) } @@ -64,6 +61,7 @@ class UnconstrainedMapGenerator( Self::Unconstrained(value) } } + """, "KeySymbol" to keySymbol, "ValueSymbol" to valueSymbol, @@ -71,10 +69,6 @@ class UnconstrainedMapGenerator( ) renderTryFromUnconstrainedForConstrained(this) - - if (!publicConstrainedTypes && shape.isDirectlyConstrained(symbolProvider)) { - renderFromFullyUnconstrainedForUnconstrained(this) - } } } @@ -91,7 +85,7 @@ class UnconstrainedMapGenerator( val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { - constrainedShapeSymbolProvider.toSymbol(valueShape) + symbolProvider.toSymbol(valueShape) } rustTemplate( @@ -158,20 +152,4 @@ class UnconstrainedMapGenerator( } } } - - private fun renderFromFullyUnconstrainedForUnconstrained(writer: RustWriter) { - // Note that if public constrained types is not enabled, then the regular `symbolProvider` produces - // "fully unconstrained" symbols for all shapes (i.e. as if the shapes didn't have any constraint traits). - writer.rustTemplate( - """ - impl #{From}<#{FullyUnconstrainedSymbol}> for $name { - fn from(fully_unconstrained: #{FullyUnconstrainedSymbol}) -> Self { - Self(fully_unconstrained.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) - } - } - """, - "From" to RuntimeType.From, - "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), - ) - } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 3b939f5b48..1c7d21add5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -39,9 +39,7 @@ class UnconstrainedUnionGenerator( val symbolProvider: RustSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constrainedShapeSymbolProvider: RustSymbolProvider, private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - private val publicConstrainedTypes: Boolean, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: UnionShape @@ -169,26 +167,23 @@ class UnconstrainedUnionGenerator( if (resolveToNonPublicConstrainedType) { rustTemplate( """ - #{PubCrateConstrainedShapeSymbol}::try_from(unconstrained) + let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained + .try_into() ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } - .map_err(Self::Error::${ConstraintViolation(member).name()})? - .into() + .map_err(Self::Error::${ConstraintViolation(member).name()})?; + constrained.into() """, "PubCrateConstrainedShapeSymbol" to pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) ) } else { - rustTemplate( + rust( """ - #{ConstrainedShapeSymbol}::try_from(unconstrained) + unconstrained + .try_into() ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } .map_err(Self::Error::${ConstraintViolation(member).name()})? - """, - "ConstrainedShapeSymbol" to constrainedShapeSymbolProvider.toSymbol(targetShape) + """ ) - - if (!publicConstrainedTypes) { - rust(".into()") - } } } else { rust("unconstrained") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 582d9daa01..f3bed2325d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJson diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 4b1bbb3324..39ade7f766 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -76,11 +76,4 @@ fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) this.targetCanReachConstrainedShape(model, symbolProvider) } else { Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - } - -fun Shape.canReachStructureShape(model: Model) = Walker(model).walkShapes(this).toSet().any { it.isStructureShape } - -fun Shape.canReachConstrainedShapeOtherThanConstrainedStructureShape(model: Model, symbolProvider: SymbolProvider) = - Walker(model).walkShapes(this).toSet() - .filter { it.isDirectlyConstrained(symbolProvider) } - .any { !it.isStructureShape } + } \ No newline at end of file diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index 42520dd68c..8db03d8f35 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -432,7 +432,7 @@ class JsonSerializerGenerator( val workingWithPublicConstrainedWrapperTupleType = codegenTarget == CodegenTarget.SERVER && keyTarget.hasPublicConstrainedWrapperTupleType(model) val keyExpression = if (workingWithPublicConstrainedWrapperTupleType) { - "$keyName.into_inner().as_str()" + "$keyName.0.as_str()" } else if (keyTarget.hasTrait()) { "$keyName.as_str()" } else { From 56dc435a776e649da8d79d52b15ae87a9056e6a9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 25 Aug 2022 18:32:15 +0200 Subject: [PATCH 147/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA->Back to square1. Until conBMap uncommented --- codegen-server-test/model/constraints.smithy | 38 +-- .../generators/PythonServerEnumGenerator.kt | 2 +- ...bCrateConstraintViolationSymbolProvider.kt | 32 ++ .../server/smithy/ServerCodegenVisitor.kt | 57 ++-- .../generators/ConstrainedMapGenerator.kt | 54 ++- .../generators/ConstrainedStringGenerator.kt | 51 ++- .../MapConstraintViolationGenerator.kt | 24 +- .../generators/ServerBuilderGenerator.kt | 321 ++++++++++++------ ...rGeneratorWithoutPublicConstrainedTypes.kt | 254 ++++++++++++++ .../smithy/generators/ServerEnumGenerator.kt | 2 +- .../ServerStructureConstrainedTraitImpl.kt | 5 +- .../UnconstrainedCollectionGenerator.kt | 24 +- .../generators/UnconstrainedMapGenerator.kt | 32 +- .../generators/UnconstrainedUnionGenerator.kt | 2 +- .../ServerHttpBoundProtocolGenerator.kt | 30 +- .../smithy/testutil/ServerTestHelpers.kt | 16 +- .../generators/ConstrainedMapGeneratorTest.kt | 2 +- .../ConstrainedStringGeneratorTest.kt | 2 +- .../generators/ServerEnumGeneratorTest.kt | 2 +- .../UnconstrainedCollectionGeneratorTest.kt | 2 +- .../UnconstrainedMapGeneratorTest.kt | 2 +- .../UnconstrainedUnionGeneratorTest.kt | 2 +- .../ConstraintViolationSymbolProvider.kt | 11 +- .../codegen/smithy/ServerCodegenContext.kt | 1 + .../UnconstrainedShapeSymbolProvider.kt | 5 +- .../smithy/generators/BuilderGenerator.kt | 18 + .../smithy/generators/StructureGenerator.kt | 47 ++- .../protocols/parse/JsonParserGenerator.kt | 20 +- .../parse/XmlBindingTraitParserGenerator.kt | 10 +- 29 files changed, 805 insertions(+), 263 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt rename {codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server => codegen/src/main/kotlin/software/amazon/smithy/rust/codegen}/smithy/ConstraintViolationSymbolProvider.kt (88%) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index f0a900d5c9..6947cab27e 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -10,20 +10,20 @@ use aws.protocols#restJson1 service ConstraintsService { operations: [ ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfEnumStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - QueryParamsTargetingMapOfListOfEnumStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // HttpPrefixHeadersTargetingMapOfEnumStringOperation, +// ConstrainedHttpBoundShapesOperation, +// ConstrainedRecursiveShapesOperation, +// // `httpQueryParams` and `httpPrefixHeaders` are structurually +// // exclusive, so we need one operation per target shape type +// // combination. +// QueryParamsTargetingLengthMapOperation, +// QueryParamsTargetingMapOfLengthStringOperation, +// QueryParamsTargetingMapOfEnumStringOperation, +// QueryParamsTargetingMapOfListOfLengthStringOperation, +// QueryParamsTargetingMapOfSetOfLengthStringOperation, +// QueryParamsTargetingMapOfListOfEnumStringOperation, +// HttpPrefixHeadersTargetingLengthMapOperation, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) +// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } @@ -206,14 +206,14 @@ structure ConA { conBMap: ConBMap, - mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, +// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - constrainedUnion: ConstrainedUnion, - enumString: EnumString, +// constrainedUnion: ConstrainedUnion, +// enumString: EnumString, - listOfLengthString: ListOfLengthString, +// listOfLengthString: ListOfLengthString, - setOfLengthString: SetOfLengthString, +// setOfLengthString: SetOfLengthString, } map MapOfLengthString { diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index 9b829f9c94..ab6072bb26 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -17,7 +17,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.util.dq diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt new file mode 100644 index 0000000000..00f5b925a3 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.rustlang.RustType +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.rustType + +/** + * This is only used when `publicConstrainedTypes` is `false`. + * + * This must wrap [ConstraintViolationSymbolProvider]. + */ +class PubCrateConstraintViolationSymbolProvider( + private val base: RustSymbolProvider, +) : WrappingSymbolProvider(base) { + override fun toSymbol(shape: Shape): Symbol { + val baseSymbol = base.toSymbol(shape) + val baseRustType = baseSymbol.rustType() + val newNamespace = baseSymbol.namespace + "_internal" + return baseSymbol.toBuilder() + .rustType(RustType.Opaque(baseRustType.name, newNamespace)) + .namespace(newNamespace, baseSymbol.namespaceDelimiter) + .build() + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 7e16fe5711..ab2af51838 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.MapConstrain import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGeneratorWithoutPublicConstrainedTypes import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerServiceGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerStructureConstrainedTraitImpl @@ -36,6 +37,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.Constrained +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider @@ -135,7 +137,7 @@ open class ServerCodegenVisitor( symbolVisitorConfig, publicConstrainedTypes = false, ), - model, service, + model, service, settings.codegenConfig.publicConstrainedTypes, ) pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, service) constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) @@ -148,6 +150,7 @@ open class ServerCodegenVisitor( settings, unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, + constraintViolationSymbolProvider, ) rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) @@ -230,18 +233,35 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val builderGenerator = ServerBuilderGenerator( + val serverBuilderGenerator = ServerBuilderGenerator( codegenContext, shape, if (shapesReachableFromOperationInputs.contains(shape)) pubCrateConstrainedShapeSymbolProvider else null ) - builderGenerator.render(writer) - writer.implBlock(shape, symbolProvider) { - builderGenerator.renderConvenienceMethod(this) + serverBuilderGenerator.render(writer) + if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { + writer.implBlock(shape, symbolProvider) { + serverBuilderGenerator.renderConvenienceMethod(this) + } } if (shapesReachableFromOperationInputs.contains(shape)) { - ServerStructureConstrainedTraitImpl(symbolProvider, shape, writer).render() + ServerStructureConstrainedTraitImpl( + symbolProvider, + codegenContext.settings.codegenConfig.publicConstrainedTypes, + shape, + writer, + ).render() + } + + if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { + val serverBuilderGeneratorWithoutPublicConstrainedTypes = + ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) + serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) + + writer.implBlock(shape, symbolProvider) { + serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) + } } } } @@ -259,11 +279,8 @@ open class ServerCodegenVisitor( rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext, pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, shape @@ -295,11 +312,9 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> UnconstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext, pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, + unconstrainedShapeSymbolProvider, unconstrainedModuleWriter, shape ).render() @@ -324,10 +339,7 @@ open class ServerCodegenVisitor( if (isDirectlyConstrained) { rustCrate.withModule(ModelsModule) { modelsModuleWriter -> ConstrainedMapGenerator( - model, - constrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, + codegenContext, modelsModuleWriter, shape, if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null @@ -338,9 +350,7 @@ open class ServerCodegenVisitor( if (isDirectlyConstrained || renderUnconstrainedMap) { rustCrate.withModule(ModelsModule) { modelsModuleWriter -> MapConstraintViolationGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, + codegenContext, modelsModuleWriter, shape ).render() @@ -387,10 +397,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> ConstrainedStringGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - settings.codegenConfig.publicConstrainedTypes, + codegenContext, writer, shape ).render() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index e169a75326..0292d2ed71 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute @@ -15,9 +14,9 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.util.expectTrait /** @@ -33,19 +32,29 @@ import software.amazon.smithy.rust.codegen.util.expectTrait * [`length` trait]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait */ class ConstrainedMapGenerator( - val model: Model, - private val constrainedSymbolProvider: RustSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - private val publicConstrainedTypes: Boolean, + val codegenContext: ServerCodegenContext, val writer: RustWriter, val shape: MapShape, private val unconstrainedSymbol: Symbol? = null, ) { + val model = codegenContext.model + val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + private val symbolProvider = codegenContext.symbolProvider + fun render() { // The `length` trait is the only constraint trait applicable to map shapes. val lengthTrait = shape.expectTrait() - val name = constrainedSymbolProvider.toSymbol(shape).name + val name = constrainedShapeSymbolProvider.toSymbol(shape).name val inner = "std::collections::HashMap<#{KeySymbol}, #{ValueSymbol}>" val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) @@ -68,8 +77,9 @@ class ConstrainedMapGenerator( ) val codegenScope = arrayOf( - "KeySymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.key.target)), - "ValueSymbol" to constrainedSymbolProvider.toSymbol(model.expectShape(shape.value.target)), + "KeySymbol" to constrainedShapeSymbolProvider.toSymbol(model.expectShape(shape.key.target)), + "ValueSymbol" to constrainedShapeSymbolProvider.toSymbol(model.expectShape(shape.value.target)), + "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, "ConstraintViolation" to constraintViolation ) @@ -113,10 +123,34 @@ class ConstrainedMapGenerator( } } } + + impl #{From}<$name> for $inner { + fn from(value: $name) -> Self { + value.into_inner() + } + } """, *codegenScope ) + if (!publicConstrainedTypes && isValueConstrained(shape, model, symbolProvider)) { + writer.rustTemplate( + """ + impl #{From}<$name> for #{FullyUnconstrainedSymbol} { + fn from(value: $name) -> Self { + value + .into_inner() + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect() + } + } + """, + *codegenScope, + "FullyUnconstrainedSymbol" to symbolProvider.toSymbol(shape), + ) + } + if (unconstrainedSymbol != null) { writer.rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 11716693d0..e76bf5e8b6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute @@ -17,12 +16,11 @@ import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.util.expectTrait -import software.amazon.smithy.rust.codegen.util.toSnakeCase /** * [ConstrainedStringGenerator] generates a wrapper tuple newtype holding a constrained `String`. @@ -30,17 +28,26 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase * the constraints. */ class ConstrainedStringGenerator( - val model: Model, - private val constrainedSymbolProvider: RustSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, - private val publicConstrainedTypes: Boolean, + val codegenContext: ServerCodegenContext, val writer: RustWriter, val shape: StringShape ) { + val model = codegenContext.model + val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + fun render() { val lengthTrait = shape.expectTrait() - val symbol = constrainedSymbolProvider.toSymbol(shape) + val symbol = constrainedShapeSymbolProvider.toSymbol(shape) val name = symbol.name val inner = RustType.String.render() val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) @@ -108,13 +115,6 @@ class ConstrainedStringGenerator( } } - pub mod ${name.toSnakeCase()} { - ##[derive(Debug, PartialEq)] - pub enum ConstraintViolation { - Length(usize), - } - } - impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; @@ -127,12 +127,31 @@ class ConstrainedStringGenerator( } } } + + impl #{From}<$name> for $inner { + fn from(value: $name) -> Self { + value.into_inner() + } + } """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, "MaybeConstrained" to symbol.makeMaybeConstrained(), "Display" to RuntimeType.Display, + "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, ) + + val constraintViolationModule = constraintViolation.namespace.split(constraintViolation.namespaceDelimiter).last() + writer.withModule(constraintViolationModule, RustMetadata(visibility = constrainedTypeVisibility)) { + rust( + """ + ##[derive(Debug, PartialEq)] + pub enum ${constraintViolation.name} { + Length(usize), + } + """ + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 7cc64685fd..33a3bb50a3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -5,23 +5,32 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.util.hasTrait class MapConstraintViolationGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + codegenContext: ServerCodegenContext, private val modelsModuleWriter: RustWriter, val shape: MapShape ) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + fun render() { val keyShape = model.expectShape(shape.key.target, StringShape::class.java) val valueShape = model.expectShape(shape.value.target) @@ -44,6 +53,9 @@ class MapConstraintViolationGenerator( modelsModuleWriter.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() ) { + // TODO We should really have two `ConstraintViolation` types here. One will just have variants for each + // constraint trait on the map shape, for use by the user. The other one will have variants if the shape's + // key or value is directly or transitively constrained, and is for use by the framework. rustTemplate( """ ##[derive(Debug, PartialEq)] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 07853c96c7..2414798f0c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -6,32 +6,44 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators 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.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.implInto import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -52,6 +64,7 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase // - This builder is not `PartialEq`. // - Always implements either From for Structure or TryFrom for Structure. // - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. +// - This builder is `pub(crate)` if `publicConstrainedTypes` is false. class ServerBuilderGenerator( codegenContext: ServerCodegenContext, private val shape: StructureShape, @@ -59,12 +72,21 @@ class ServerBuilderGenerator( ) { private val takeInUnconstrainedTypes = pubCrateConstrainedShapeSymbolProvider != null private val model = codegenContext.model + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val symbolProvider = codegenContext.symbolProvider private val constraintViolationSymbolProvider = - ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - private val moduleName = shape.builderSymbol(symbolProvider).namespace.split("::").last() + private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) + private val moduleName = builderSymbol.namespace.split(builderSymbol.namespaceDelimiter).last() private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) private val codegenScope = arrayOf( @@ -76,8 +98,14 @@ class ServerBuilderGenerator( ) fun render(writer: RustWriter) { + val visibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } + writer.docs("See #D.", structureSymbol) - writer.withModule(moduleName) { + writer.withModule(moduleName, RustMetadata(visibility = visibility)) { renderBuilder(this) } } @@ -89,7 +117,16 @@ class ServerBuilderGenerator( Attribute.NonExhaustive.render(writer) val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name writer.rustBlock("pub enum $constraintViolationSymbolName") { - constraintViolations().forEach { renderConstraintViolation(this, it) } + constraintViolations().forEach { + renderConstraintViolation( + this, + it, + model, + constraintViolationSymbolProvider, + symbolProvider, + structureSymbol, + ) + } } renderImplDisplayConstraintViolation(writer) @@ -122,9 +159,11 @@ class ServerBuilderGenerator( writer.rustBlock("impl Builder") { for (member in members) { - renderBuilderMemberFn(this, member) + if (publicConstrainedTypes) { + renderBuilderMemberFn(this, member) + } - if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (takeInUnconstrainedTypes) { renderBuilderMemberSetterFn(this, member) } } @@ -143,7 +182,7 @@ class ServerBuilderGenerator( } else { "ConstraintViolation::${it.name()}" } - rust("""$arm => write!(f, "${constraintViolationMessage(it)}"),""") + rust("""$arm => write!(f, "${constraintViolationMessage(it, symbolProvider, structureSymbol)}"),""") } } } @@ -177,13 +216,18 @@ class ServerBuilderGenerator( ) } - private fun renderBuildFn(implBlockWriter: RustWriter) { - val returnType = when (isBuilderFallible) { - true -> "Result<${implBlockWriter.format(structureSymbol)}, ConstraintViolation>" - false -> implBlockWriter.format(structureSymbol) + private fun buildFnReturnType() = writable { + if (isBuilderFallible) { + rust("Result<#T, ConstraintViolation>", structureSymbol) + } else { + rust("#T", structureSymbol) } + } + + private fun renderBuildFn(implBlockWriter: RustWriter) { implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) if (isBuilderFallible) { + // TODO Docs not accurate. Structure may not have any optional members. implBlockWriter.docs( """ The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. @@ -195,7 +239,15 @@ class ServerBuilderGenerator( implBlockWriter.docs("If the builder fails, it will return the _first_ encountered [`ConstraintViolation`].") } } - implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { + // TODO Could be single block + implBlockWriter.rustBlockTemplate("pub fn build(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { + rust("self.build_enforcing_all_constraints()") + } + renderBuildEnforcingAllConstraintsFn(implBlockWriter) + } + + private fun renderBuildEnforcingAllConstraintsFn(implBlockWriter: RustWriter) { + implBlockWriter.rustBlockTemplate("fn build_enforcing_all_constraints(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { coreBuilder(this) } @@ -203,7 +255,6 @@ class ServerBuilderGenerator( } fun renderConvenienceMethod(implBlock: RustWriter) { - val builderSymbol = shape.builderSymbol(symbolProvider) implBlock.docs("Creates a new builder-style object to manufacture #D.", structureSymbol) implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { write("#T::default()", builderSymbol) @@ -212,7 +263,7 @@ class ServerBuilderGenerator( private fun renderBuilderMember(writer: RustWriter, member: MemberShape) { val memberSymbol = builderMemberSymbol(member) - val memberName = symbolProvider.toMemberName(member) + val memberName = constrainedShapeSymbolProvider.toMemberName(member) // Builder members are crate-public to enable using them directly in serializers/deserializers. // During XML deserialization, `builder..take` is used to append to lists and maps. writer.write("pub(crate) $memberName: #T,", memberSymbol) @@ -223,6 +274,9 @@ class ServerBuilderGenerator( * as the shape member's type. * * This method is meant for use by the user; it is not used by the generated crate's (de)serializers. + * + * This method is only generated when `publicConstrainedTypes` is `true`. Otherwise, the user has at their disposal + * the method from [ServerBuilderGeneratorWithoutPublicConstrainedTypes]. */ private fun renderBuilderMemberFn( writer: RustWriter, @@ -235,6 +289,8 @@ class ServerBuilderGenerator( val wrapInMaybeConstrained = takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider) writer.documentShape(member, model) + writer.deprecatedShape(member) + if (hasBox && wrapInMaybeConstrained) { // In the case of recursive shapes, the member might be boxed. If so, and the member is also constrained, the // implementation of this function needs to immediately unbox the value to wrap it in `MaybeConstrained`, @@ -245,25 +301,47 @@ class ServerBuilderGenerator( Attribute.Custom("allow(clippy::boxed_local)").render(writer) } writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { - rust("self.$memberName = ") - conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - if (wrapInMaybeConstrained) { - val maybeConstrainedConstrained = "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). + withBlock("self.$memberName = ", "; self") { + conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { + val targetShape = model.expectShape(member.target) + // If constrained types are not public and the target shape is one that would generate a public constrained + // type had the `publicConstrainedTypes` setting been enabled, then we need to: + // 1. constrain the input type into the corresponding `pub(crate)` unconstrained types first; and + // 2. store the resulting value in a `MaybeConstrained::Unconstrained` variant. + // This condition is calculated once here and used later when the above two steps are performed. + // Note we explicitly opt out when the target shape is a structure shape or a string shape with the + // `enum` trait, since in those cases public constrained types are _always_ generated, regardless of + // whether `publicConstrainedTypes` is enabled (remember structure shapes are directly constrained + // when at least one of their members is non-optional). + // TODO I should be able to remove this whole thing because now this method is only called when `publicConstrainedTypes` is `true`. + val isInputFullyUnconstrained = !publicConstrainedTypes && + targetShape.canReachConstrainedShape(model, symbolProvider) && + !(targetShape is StringShape && targetShape.hasTrait()) && + targetShape !is StructureShape + + val maybeConstrainedVariant = if (isInputFullyUnconstrained) + // Step 2 above. + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" + else { + "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" + } + var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" - conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { - conditionalBlock("Box::new(", ")", conditional = hasBox) { - rust("$maybeConstrainedConstrained($varExpr)") + + if (wrapInMaybeConstrained) { + // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). + conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { + conditionalBlock("Box::new(", ")", conditional = hasBox) { + rust("$maybeConstrainedVariant($varExpr)") + } } + } else { + write("input") } - } else { - write("input") } } - rust(";") - rust("self") } } @@ -320,67 +398,9 @@ class ServerBuilderGenerator( } } - /** - * The kinds of constraint violations that can occur when building the builder. - */ - enum class ConstraintViolationKind { - // A field is required but was not provided. - MISSING_MEMBER, - // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. - CONSTRAINED_SHAPE_FAILURE, - } - - data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { - fun name() = when (kind) { - ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ConstraintViolation" - } - - /** - * Whether the constraint violation is a Rust tuple struct with one element. - */ - fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE - } - - private fun renderConstraintViolation(writer: RustWriter, constraintViolation: ConstraintViolation) = - when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> { - writer.docs("${constraintViolationMessage(constraintViolation).replaceFirstChar { it.uppercase() }}.") - writer.rust("${constraintViolation.name()},") - } - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { - val targetShape = model.expectShape(constraintViolation.forMember.target) - - val constraintViolationSymbol = - constraintViolationSymbolProvider.toSymbol(targetShape) - // If the corresponding structure's member is boxed, box this constraint violation symbol too. - .letIf(constraintViolation.forMember.hasTrait()) { - it.makeRustBoxed() - } - - // Note we cannot express the inner constraint violation as `>::Error`, because `T` might - // be `pub(crate)` and that would leak `T` in a public interface. - writer.docs("${constraintViolationMessage(constraintViolation)}.") - Attribute.DocHidden.render(writer) - writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) - } - } - - /** - * A message for a `ConstraintViolation` variant. This is used in both Rust documentation and the `Display` trait implementation. - */ - private fun constraintViolationMessage(constraintViolation: ConstraintViolation): String { - val memberName = symbolProvider.toMemberName(constraintViolation.forMember) - return when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" - // TODO Nest errors. - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" - } - } - private fun constraintViolations() = members.flatMap { member -> listOfNotNull( - builderMissingFieldForMember(member), + builderMissingFieldForMember(member, symbolProvider), builderConstraintViolationForMember(member), ) } @@ -395,18 +415,6 @@ class ServerBuilderGenerator( null } - /** - * Returns the builder failure associated with the `member` field if it is `required`. - */ - private fun builderMissingFieldForMember(member: MemberShape) = - // TODO(https://github.com/awslabs/smithy-rs/issues/1302): We go through the symbol provider because - // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. - if (symbolProvider.toSymbol(member).isOptional()) { - null - } else { - ConstraintViolation(member, ConstraintViolationKind.MISSING_MEMBER) - } - private fun renderTryFromBuilderImpl(writer: RustWriter) { writer.rustTemplate( """ @@ -442,7 +450,7 @@ class ServerBuilderGenerator( private fun builderMemberSymbol(member: MemberShape): Symbol = if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { val strippedOption = if (member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider)) { - symbolProvider.toSymbol(member) + constrainedShapeSymbolProvider.toSymbol(member) } else { pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } @@ -461,7 +469,7 @@ class ServerBuilderGenerator( // Ensure we always end up with an `Option`. .makeOptional() } else { - symbolProvider.toSymbol(member).makeOptional() + constrainedShapeSymbolProvider.toSymbol(member).makeOptional() } /** @@ -477,7 +485,9 @@ class ServerBuilderGenerator( * a) `Option`; or * b) `T`. * - * `U` is equal to `T` when the shape for `U` has a constraint trait or the member shape is a structure shape. + * `U` is equal to `T` when: + * - the shape for `U` has a constraint trait and `publicConstrainedTypes` is `true`; or + * - the member shape is a structure or union shape. * Otherwise, `U` is always a `pub(crate)` tuple newtype holding `T`. * * For each member, this function first safely unwraps case 1. into 2., then converts `U` into `T` if necessary, @@ -517,16 +527,28 @@ class ServerBuilderGenerator( #{MaybeConstrained}::Unconstrained(x) => x.try_into(), }) .map(|res| - res${ if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())" } + res${if (constrainedTypeHoldsFinalType(member)) "" else ".map(|v| v.into())"} .map_err(ConstraintViolation::${constraintViolation.name()}) ) .transpose()? """, - *codegenScope + *codegenScope, ) + + // Constrained types are not public and this is a member shape that would have generated a + // public constrained type, were the setting to be enabled. + // We've just checked the constraints hold by going through the non-public + // constrained type, but the user wants to work with the unconstrained type, so we have to + // unwrap it. + if (!publicConstrainedTypes && member.hasPublicConstrainedWrapperTupleType(model)) { + rust( + ".map(|v: #T| v.into())", + constrainedShapeSymbolProvider.toSymbol(model.expectShape(member.target)), + ) + } } } - builderMissingFieldForMember(member)?.also { + builderMissingFieldForMember(member, symbolProvider)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") } } @@ -534,3 +556,92 @@ class ServerBuilderGenerator( } } } + +/** + * The kinds of constraint violations that can occur when building the builder. + */ +enum class ConstraintViolationKind { + // A field is required but was not provided. + MISSING_MEMBER, + // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. + CONSTRAINED_SHAPE_FAILURE, +} + +data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { + fun name() = when (kind) { + ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ConstraintViolation" + } + + /** + * Whether the constraint violation is a Rust tuple struct with one element. + */ + fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE +} + +/** + * A message for a `ConstraintViolation` variant. This is used in both Rust documentation and the `Display` trait implementation. + */ +fun constraintViolationMessage( + constraintViolation: ConstraintViolation, + symbolProvider: RustSymbolProvider, + structureSymbol: Symbol, +): String { + val memberName = symbolProvider.toMemberName(constraintViolation.forMember) + return when (constraintViolation.kind) { + ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" + // TODO Nest errors. + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" + } +} + +// TODO Extract to common +/** + * Returns the builder failure associated with the [member] field if it is `required`. + */ +fun builderMissingFieldForMember(member: MemberShape, symbolProvider: RustSymbolProvider) = +// TODO(https://github.com/awslabs/smithy-rs/issues/1302): We go through the symbol provider because + // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. + if (symbolProvider.toSymbol(member).isOptional()) { + null + } else { + ConstraintViolation(member, ConstraintViolationKind.MISSING_MEMBER) + } + +fun renderConstraintViolation( + writer: RustWriter, + constraintViolation: ConstraintViolation, + model: Model, + constraintViolationSymbolProvider: SymbolProvider, + symbolProvider: RustSymbolProvider, + structureSymbol: Symbol, +) = + when (constraintViolation.kind) { + ConstraintViolationKind.MISSING_MEMBER -> { + writer.docs( + "${constraintViolationMessage( + constraintViolation, + symbolProvider, + structureSymbol, + ).replaceFirstChar { it.uppercase() }}.", + ) + writer.rust("${constraintViolation.name()},") + } + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { + val targetShape = model.expectShape(constraintViolation.forMember.target) + + val constraintViolationSymbol = + constraintViolationSymbolProvider.toSymbol(targetShape) + // If the corresponding structure's member is boxed, box this constraint violation symbol too. + .letIf(constraintViolation.forMember.hasTrait()) { + it.makeRustBoxed() + } + + // Note we cannot express the inner constraint violation as `>::Error`, because `T` might + // be `pub(crate)` and that would leak `T` in a public interface. + writer.docs("${constraintViolationMessage(constraintViolation, symbolProvider, structureSymbol)}.") + Attribute.DocHidden.render(writer) + writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) + } + } + diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt new file mode 100644 index 0000000000..005b9e4347 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -0,0 +1,254 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.rustlang.Attribute +import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape +import software.amazon.smithy.rust.codegen.rustlang.docs +import software.amazon.smithy.rust.codegen.rustlang.documentShape +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.withBlock +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.smithy.isOptional +import software.amazon.smithy.rust.codegen.smithy.makeOptional + +/** + * TODO Docs + * This builder only enforces the `required` trait. + * + * This builder is always public. + */ +class ServerBuilderGeneratorWithoutPublicConstrainedTypes( + codegenContext: ServerCodegenContext, + private val shape: StructureShape, +) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val constraintViolationSymbolProvider = + ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val members: List = shape.allMembers.values.toList() + private val structureSymbol = symbolProvider.toSymbol(shape) + // TODO moduleName + private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, false) + private val moduleName = builderSymbol.namespace.split("::").last() + private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilderWithoutPublicConstrainedTypes(shape, symbolProvider) + + private val codegenScope = arrayOf( + "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), + "Structure" to structureSymbol, + "From" to RuntimeType.From, + "TryFrom" to RuntimeType.TryFrom, + "MaybeConstrained" to RuntimeType.MaybeConstrained() + ) + + fun render(writer: RustWriter) { + writer.docs("See #D.", structureSymbol) + writer.withModule(moduleName) { + renderBuilder(this) + } + } + + private fun renderBuilder(writer: RustWriter) { + if (isBuilderFallible) { + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) + writer.docs("Holds one variant for each of the ways the builder can fail.") + val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name + writer.rustBlock("pub enum $constraintViolationSymbolName") { + constraintViolations().forEach { + renderConstraintViolation( + this, + it, + model, + constraintViolationSymbolProvider, + symbolProvider, + structureSymbol, + ) + } + } + + renderImplDisplayConstraintViolation(writer) + writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) + + renderTryFromBuilderImpl(writer) + } else { + renderFromBuilderImpl(writer) + } + + writer.docs("A builder for #D.", structureSymbol) + // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. + // TODO Manually implement `Default` so that we can add custom docs. + val baseDerives = structureSymbol.expectRustMetadata().derives + val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default + baseDerives.copy(derives = derives).render(writer) + writer.rustBlock("pub struct Builder") { + members.forEach { renderBuilderMember(this, it) } + } + + writer.rustBlock("impl Builder") { + for (member in members) { + renderBuilderMemberFn(this, member) + } + renderBuildFn(this) + } + } + + // TODO Extract + // TODO This impl does not take into account sensitive trait. + private fun renderImplDisplayConstraintViolation(writer: RustWriter) { + writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { + rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("match self") { + constraintViolations().forEach { + val arm = if (it.hasInner()) { + "ConstraintViolation::${it.name()}(_)" + } else { + "ConstraintViolation::${it.name()}" + } + rust("""$arm => write!(f, "${constraintViolationMessage(it, symbolProvider, structureSymbol)}"),""") + } + } + } + } + } + + // TODO Extract + private fun buildFnReturnType() = writable { + if (isBuilderFallible) { + rust("Result<#T, ConstraintViolation>", structureSymbol) + } else { + rust("#T", structureSymbol) + } + } + + private fun renderBuildFn(implBlockWriter: RustWriter) { + implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) + if (isBuilderFallible) { + implBlockWriter.docs( + """ + The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. + """, + structureSymbol + ) + } + // TODO Could be single block + implBlockWriter.rustBlockTemplate("pub fn build(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { + rust("self.build_enforcing_required_and_enum_traits()") + } + renderBuildEnforcingRequiredAndEnumTraitsFn(implBlockWriter) + } + + private fun renderBuildEnforcingRequiredAndEnumTraitsFn(implBlockWriter: RustWriter) { + implBlockWriter.rustBlockTemplate("fn build_enforcing_required_and_enum_traits(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { + conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { + coreBuilder(this) + } + } + } + + private fun coreBuilder(writer: RustWriter) { + writer.rustBlock("#T", structureSymbol) { + for (member in members) { + val memberName = symbolProvider.toMemberName(member) + + withBlock("$memberName: self.$memberName", ",") { + builderMissingFieldForMember(member, symbolProvider)?.also { + rust(".ok_or(ConstraintViolation::${it.name()})?") + } + } + } + } + } + + fun renderConvenienceMethod(implBlock: RustWriter) { + implBlock.docs("Creates a new builder-style object to manufacture #D.", structureSymbol) + implBlock.rustBlock("pub fn builder() -> #T", builderSymbol) { + write("#T::default()", builderSymbol) + } + } + + private fun renderBuilderMember(writer: RustWriter, member: MemberShape) { + val memberSymbol = builderMemberSymbol(member) + val memberName = symbolProvider.toMemberName(member) + // Builder members are crate-public to enable using them directly in serializers/deserializers. + // During XML deserialization, `builder..take` is used to append to lists and maps. + writer.write("pub(crate) $memberName: #T,", memberSymbol) + } + + /** + * Render a `foo` method to set shape member `foo`. The caller must provide a value with the exact same type + * as the shape member's type. + * + * This method is meant for use by the user; it is not used by the generated crate's (de)serializers. + */ + private fun renderBuilderMemberFn(writer: RustWriter, member: MemberShape) { + val memberSymbol = symbolProvider.toSymbol(member) + val memberName = symbolProvider.toMemberName(member) + + writer.documentShape(member, model) + writer.deprecatedShape(member) + + writer.rustBlock("pub fn $memberName(mut self, input: #T) -> Self", memberSymbol) { + withBlock("self.$memberName = ", "; self") { + conditionalBlock("Some(", ")", conditional = !memberSymbol.isOptional()) { + rust("input") + } + } + } + } + + private fun constraintViolations() = members.flatMap { member -> + listOfNotNull(builderMissingFieldForMember(member, symbolProvider)) + } + + private fun renderTryFromBuilderImpl(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{TryFrom} for #{Structure} { + type Error = ConstraintViolation; + + fn try_from(builder: Builder) -> Result { + builder.build() + } + } + """, + *codegenScope + ) + } + + private fun renderFromBuilderImpl(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{From} for #{Structure} { + fn from(builder: Builder) -> Self { + builder.build() + } + } + """, + *codegenScope + ) + } + + /** + * Returns the symbol for a builder's member. + */ + private fun builderMemberSymbol(member: MemberShape): Symbol = symbolProvider.toSymbol(member).makeOptional() +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 94939879fd..1e030fee11 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt index 5be20d9618..269f3ca97a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -10,10 +10,11 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol class ServerStructureConstrainedTraitImpl( private val symbolProvider: RustSymbolProvider, + private val publicConstrainedTypes: Boolean, private val shape: StructureShape, private val writer: RustWriter ) { @@ -26,7 +27,7 @@ class ServerStructureConstrainedTraitImpl( """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "Structure" to symbolProvider.toSymbol(shape), - "Builder" to shape.builderSymbol(symbolProvider) + "Builder" to shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 1752099769..3cec3b99a9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -5,31 +5,39 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs class UnconstrainedCollectionGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: CollectionShape ) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 210f102c5b..200950a377 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata @@ -14,9 +13,9 @@ import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained @@ -24,24 +23,34 @@ import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs class UnconstrainedMapGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, val shape: MapShape ) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) private val name = symbol.name + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } private val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) - private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) - private val valueShape = model.expectShape(shape.value.target) + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val constrainedSymbol = if (shape.isDirectlyConstrained(symbolProvider)) { - symbolProvider.toSymbol(shape) + constrainedShapeSymbolProvider.toSymbol(shape) } else { pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) } + private val keyShape = model.expectShape(shape.key.target, StringShape::class.java) + private val valueShape = model.expectShape(shape.value.target) fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) @@ -78,6 +87,7 @@ class UnconstrainedMapGenerator( rustBlock("fn try_from(value: $name) -> Result") { if (isKeyConstrained(keyShape, symbolProvider) || isValueConstrained(valueShape, model, symbolProvider)) { + // TODO I think this breaks if the value shape is a constrained enum (?) Add protocol test. val resolveToNonPublicConstrainedValueType = isValueConstrained(valueShape, model, symbolProvider) && !valueShape.isDirectlyConstrained(symbolProvider) && @@ -85,7 +95,7 @@ class UnconstrainedMapGenerator( val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { - symbolProvider.toSymbol(valueShape) + constrainedShapeSymbolProvider.toSymbol(valueShape) } rustTemplate( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 1c7d21add5..3c8313dc95 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index a1d4e46199..099f30e97a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -49,13 +49,13 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor @@ -675,7 +675,13 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) val structuredDataParser = protocol.structuredDataParser(operationShape) Attribute.AllowUnusedMut.render(this) - rust("let mut input = #T::default();", inputShape.builderSymbol(symbolProvider)) + rust( + "let mut input = #T::default();", + inputShape.serverBuilderSymbol( + symbolProvider, + !codegenContext.settings.codegenConfig.publicConstrainedTypes, + ), + ) val parser = structuredDataParser.serverInputParser(operationShape) if (parser != null) { val expectedRequestContentType = httpBindingResolver.requestContentType(operationShape) @@ -703,7 +709,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rust( """ { - input = input.${member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}(${ + input = input.${member.deserializerBuilderSetterName(codegenContext.target)}(${ if (symbolProvider.toSymbol(binding.member).isOptional()) { "Some(value)" } else { @@ -855,13 +861,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val deserializer = generateParseStrFn(binding, true) rustTemplate( """ - input = input.${ - binding.member.deserializerBuilderSetterName( - model, - symbolProvider, - codegenContext.target - ) - }( + input = input.${binding.member.deserializerBuilderSetterName(codegenContext.target)}( #{deserializer}(m$index)? ); """, @@ -954,7 +954,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !seen_$memberName && k == "${it.locationName}" { - input = input.${it.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}( + input = input.${it.member.deserializerBuilderSetterName(codegenContext.target)}( #{deserializer}(&v)? ); seen_$memberName = true; @@ -1044,7 +1044,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } if (queryParamsBinding != null) { val isOptional = unconstrainedShapeSymbolProvider.toSymbol(queryParamsBinding.member).isOptional() - withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(model, symbolProvider, codegenContext.target)}(", ");") { + withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(codegenContext.target)}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { write("query_params") } @@ -1060,11 +1060,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustBlock("if !$memberName.is_empty()") { withBlock( "input = input.${ - binding.member.deserializerBuilderSetterName( - model, - unconstrainedShapeSymbolProvider, - codegenContext.target - ) + binding.member.deserializerBuilderSetterName(codegenContext.target) }(", ");" ) { conditionalBlock("Some(", ")", conditional = isOptional) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 28614ffe2f..3476f41795 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -13,6 +13,8 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenConfig @@ -82,8 +84,10 @@ fun serverTestCodegenContext( ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build() val symbolProvider = serverTestSymbolProvider(model, serviceShape) - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, service) + val unconstrainedShapeSymbolProvider = + UnconstrainedShapeSymbolProvider(symbolProvider, model, service, settings.codegenConfig.publicConstrainedTypes) val constrainedShapeSymbolProvider = serverTestSymbolProvider(model, publicConstrainedTypesEnabled = true) + val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) val protocol = protocolShapeId ?: ShapeId.from("test#Protocol") return ServerCodegenContext( model, @@ -93,6 +97,7 @@ fun serverTestCodegenContext( settings, unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, + constraintViolationSymbolProvider ) } @@ -101,7 +106,14 @@ fun serverTestCodegenContext( */ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { StructureGenerator(model, symbolProvider, writer, this).render(CodegenTarget.SERVER) - val modelBuilder = ServerBuilderGenerator(serverTestCodegenContext(model), this) + val serverCodegenContext = serverTestCodegenContext(model) + val pubCrateConstrainedShapeSymbolProvider = + PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serverCodegenContext.serviceShape) + val modelBuilder = ServerBuilderGenerator( + serverCodegenContext, + this, + pubCrateConstrainedShapeSymbolProvider, + ) modelBuilder.render(writer) writer.implBlock(this, symbolProvider) { modelBuilder.renderConvenienceMethod(this) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 1e5ccc8e56..39a0243c41 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 1f825d1cb2..68ca5cc324 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.testutil.TestWorkspace diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 9489e61dac..854cae22ff 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -9,7 +9,7 @@ import io.kotest.matchers.string.shouldNotContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 72fc6010ae..5d813bf638 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index a88f844a83..ce454a3495 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index fb11209272..603649bc31 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.server.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt similarity index 88% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt index f4fcf55daf..64cda71d08 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.rust.codegen.server.smithy +package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model @@ -16,14 +16,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType -import software.amazon.smithy.rust.codegen.smithy.Models -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.Unconstrained -import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.smithy.contextName import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.toSnakeCase /** @@ -101,7 +94,7 @@ class ConstraintViolationSymbolProvider( .rustType(rustType) .name(rustType.name) .namespace(rustType.namespace, "::") - .definitionFile(Unconstrained.filename) + .definitionFile(builderSymbol.definitionFile) .build() } is StringShape -> { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt index 378c1a8b15..80b702f897 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt @@ -27,6 +27,7 @@ data class ServerCodegenContext( override val settings: ServerRustSettings, val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, val constrainedShapeSymbolProvider: RustSymbolProvider, + val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, ) : CoreCodegenContext( model, symbolProvider, serviceShape, protocol, settings, CodegenTarget.SERVER, ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index d7cb42f19c..aa342ed495 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.rustlang.RustType -import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -71,6 +71,7 @@ class UnconstrainedShapeSymbolProvider( private val base: RustSymbolProvider, private val model: Model, private val serviceShape: ServiceShape, + private val publicConstrainedTypes: Boolean, ) : WrappingSymbolProvider(base) { private val nullableIndex = NullableIndex.of(model) @@ -106,7 +107,7 @@ class UnconstrainedShapeSymbolProvider( } is StructureShape -> { if (shape.canReachConstrainedShape(model, base)) { - shape.builderSymbol(base) + shape.serverBuilderSymbol(base, !publicConstrainedTypes) } else { base.toSymbol(shape) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index aec1a7a719..ae74c747aa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -47,6 +47,24 @@ fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { .build() } +// TODO Place in a server file. +fun StructureShape.serverBuilderSymbol(symbolProvider: RustSymbolProvider, pubCrate: Boolean): Symbol { + val structureSymbol = symbolProvider.toSymbol(this) + val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + + if (pubCrate) { + "_internal" + } else { + "" + } + val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(structureSymbol.definitionFile) + .build() +} + fun RuntimeConfig.operationBuildError() = RuntimeType.operationModule(this).member("BuildError") fun RuntimeConfig.serializationError() = RuntimeType.operationModule(this).member("SerializationError") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index d32439796a..314bfc1c82 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -24,8 +24,10 @@ import software.amazon.smithy.rust.codegen.rustlang.isDeref import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.canUseDefault import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata @@ -33,7 +35,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.error.ErrorGenerato import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.renamedFrom import software.amazon.smithy.rust.codegen.smithy.rustType -import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.getTrait @@ -58,17 +59,30 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S * The name of the builder's setter the deserializer should use. * Setter names will never hit a reserved word and therefore never need escaping. */ -fun MemberShape.deserializerBuilderSetterName(model: Model, symbolProvider: SymbolProvider, codegenTarget: CodegenTarget): String { - if (codegenTarget == CodegenTarget.CLIENT) { - return this.setterName() +fun MemberShape.deserializerBuilderSetterName(codegenTarget: CodegenTarget) = + when (codegenTarget) { + CodegenTarget.CLIENT -> this.setterName() + CodegenTarget.SERVER -> "set_${this.memberName.toSnakeCase()}" } - return if (this.targetCanReachConstrainedShape(model, symbolProvider)) { - "set_${this.memberName.toSnakeCase()}" - } else { - this.memberName.toSnakeCase() +// TODO Cleanup (old implementation of above extension function). +// return if (this.targetCanReachConstrainedShape(model, symbolProvider)) { +// "set_${this.memberName.toSnakeCase()}" +// } else { +// this.memberName.toSnakeCase() +// } + +fun StructureShape.builderSymbol( + coreCodegenContext: CoreCodegenContext, + symbolProvider: RustSymbolProvider, +) = + when (coreCodegenContext.target) { + CodegenTarget.CLIENT -> this.builderSymbol(symbolProvider) + CodegenTarget.SERVER -> { + val publicConstrainedTypes = (coreCodegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes + this.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) + } } -} open class StructureGenerator( val model: Model, @@ -121,6 +135,21 @@ open class StructureGenerator( .map { symbolProvider.toSymbol(it) } .any { !it.isOptional() } } + + /** + * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGeneratorWithoutPublicConstrainedTypes], + * requires a fallible builder to be constructed. + * + * This builder only enforces the `required` trait. + */ + fun serverHasFallibleBuilderWithoutPublicConstrainedTypes( + structureShape: StructureShape, + symbolProvider: SymbolProvider, + ) = + structureShape + .members() + .map { symbolProvider.toSymbol(it) } + .any { !it.isOptional() } } /** diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index e084df3b27..ec124080fa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -101,14 +101,14 @@ class JsonParserGenerator( */ private fun structureParser( fnName: String, - structureShape: StructureShape, + builderSymbol: Symbol, includedMembers: List, ): RuntimeType { return RuntimeType.forInlineFun(fnName, jsonDeserModule) { val unusedMut = if (includedMembers.isEmpty()) "##[allow(unused_mut)] " else "" it.rustBlockTemplate( "pub(crate) fn $fnName(value: &[u8], ${unusedMut}mut builder: #{Builder}) -> Result<#{Builder}, #{Error}>", - "Builder" to structureShape.builderSymbol(symbolProvider), + "Builder" to builderSymbol, *codegenScope, ) { rustTemplate( @@ -166,7 +166,7 @@ class JsonParserGenerator( } val outputShape = operationShape.outputShape(model) val fnName = symbolProvider.deserializeFunctionName(operationShape) - return structureParser(fnName, outputShape, httpDocumentMembers) + return structureParser(fnName, builderSymbol(outputShape), httpDocumentMembers) } override fun errorParser(errorShape: StructureShape): RuntimeType? { @@ -174,7 +174,7 @@ class JsonParserGenerator( return null } val fnName = symbolProvider.deserializeFunctionName(errorShape) + "_json_err" - return structureParser(fnName, errorShape, errorShape.members().toList()) + return structureParser(fnName, builderSymbol(errorShape), errorShape.members().toList()) } private fun orEmptyJson(): RuntimeType = RuntimeType.forInlineFun("or_empty_doc", jsonDeserModule) { @@ -198,7 +198,7 @@ class JsonParserGenerator( } val inputShape = operationShape.inputShape(model) val fnName = symbolProvider.deserializeFunctionName(operationShape) - return structureParser(fnName, inputShape, includedMembers) + return structureParser(fnName, builderSymbol(inputShape), includedMembers) } private fun RustWriter.expectEndOfTokenStream() { @@ -219,7 +219,7 @@ class JsonParserGenerator( CodegenTarget.CLIENT -> { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + member.deserializerBuilderSetterName(codegenTarget) }(", ");" ) { deserializeMember(member) @@ -229,7 +229,7 @@ class JsonParserGenerator( if (symbolProvider.toSymbol(member).isOptional()) { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + member.deserializerBuilderSetterName(codegenTarget) }(", ");" ) { deserializeMember(member) @@ -241,7 +241,7 @@ class JsonParserGenerator( """ { builder = builder.${ - member.deserializerBuilderSetterName(model, symbolProvider, codegenTarget) + member.deserializerBuilderSetterName(codegenTarget) }(v); } """ @@ -439,7 +439,7 @@ class JsonParserGenerator( ) { startObjectOrNull { Attribute.AllowUnusedMut.render(this) - rustTemplate("let mut builder = #{Shape}::builder();", *codegenScope, "Shape" to symbol) + rustTemplate("let mut builder = #{Builder}::default();", *codegenScope, "Builder" to builderSymbol(shape)) deserializeStructInner(shape.members()) // Only call `build()` if the builder is not fallible. Otherwise, return the builder. if (returnBuilder) { @@ -597,4 +597,6 @@ class JsonParserGenerator( } else { false to symbolProvider.toSymbol(shape) } + + private fun builderSymbol(shape: StructureShape) = shape.builderSymbol(coreCodegenContext, symbolProvider) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index f100b77053..a58bf48920 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -66,7 +66,7 @@ data class OperationWrapperContext( ) class XmlBindingTraitParserGenerator( - coreCodegenContext: CoreCodegenContext, + private val coreCodegenContext: CoreCodegenContext, private val xmlErrors: RuntimeType, private val writeOperationWrapper: RustWriter.(OperationWrapperContext, OperationInnerWriteable) -> Unit, ) : StructuredDataParserGenerator { @@ -184,7 +184,7 @@ class XmlBindingTraitParserGenerator( Attribute.AllowUnusedMut.render(it) it.rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - outputShape.builderSymbol(symbolProvider), + builderSymbol(outputShape), xmlError, ) { rustTemplate( @@ -216,7 +216,7 @@ class XmlBindingTraitParserGenerator( Attribute.AllowUnusedMut.render(it) it.rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - errorShape.builderSymbol(symbolProvider), + builderSymbol(errorShape), xmlError, ) { val members = errorShape.errorXmlMembers() @@ -250,7 +250,7 @@ class XmlBindingTraitParserGenerator( Attribute.AllowUnusedMut.render(it) it.rustBlock( "pub fn $fnName(inp: &[u8], mut builder: #1T) -> Result<#1T, #2T>", - inputShape.builderSymbol(symbolProvider), + builderSymbol(inputShape), xmlError, ) { rustTemplate( @@ -728,4 +728,6 @@ class XmlBindingTraitParserGenerator( private fun StructureShape.xmlMembers(): XmlMemberIndex { return XmlMemberIndex.fromMembers(this.members().toList()) } + + private fun builderSymbol(shape: StructureShape) = shape.builderSymbol(coreCodegenContext, symbolProvider) } From 24da7b1d317f7f520eceabe3bbc1399c241e715a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 29 Aug 2022 17:24:57 +0200 Subject: [PATCH 148/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA->Until mapOfMapOfListOfListOfConB uncommented --- codegen-server-test/model/constraints.smithy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 6947cab27e..cd2447d90c 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -206,7 +206,7 @@ structure ConA { conBMap: ConBMap, -// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, + mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, // constrainedUnion: ConstrainedUnion, // enumString: EnumString, From 183fe57ab9bfbe09ced67dee8775fe97d172abe7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 30 Aug 2022 17:29:29 +0200 Subject: [PATCH 149/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA->Until constrainedUnion uncommented, with enumString commented --- codegen-server-test/model/constraints.smithy | 5 ++- .../server/smithy/ServerCodegenVisitor.kt | 5 +-- .../PubCrateConstrainedMapGenerator.kt | 5 +++ .../generators/UnconstrainedUnionGenerator.kt | 39 ++++++++++++------- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index cd2447d90c..22e52ab32f 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -208,8 +208,9 @@ structure ConA { mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, -// constrainedUnion: ConstrainedUnion, + constrainedUnion: ConstrainedUnion, // enumString: EnumString, + // TODO Uncomment enumString from union when above line is uncommented. // listOfLengthString: ListOfLengthString, @@ -264,7 +265,7 @@ string MediaTypeLengthString /// A union with constrained members. union ConstrainedUnion { - enumString: EnumString, + //enumString: EnumString, lengthString: LengthString, constrainedStructure: ConB, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index ab2af51838..2d517fc072 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -427,11 +427,8 @@ open class ServerCodegenVisitor( rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedUnionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext, pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, shape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 564aa1153f..d979d20620 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -21,6 +21,11 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained +// TODO I'm looking at this class and `ConstrainedMapGenerator` and cannot help but think that we could dispense with +// the stuff `PubCrate*` (i.e. the entire `constrained module), by just generating the `Constrained*` shapes and +// marking them `pub(crate)`. After all, when `publicConstrainedTypes` is `false`, we're still generating the +// constrained types and using them in the deser path, but we're marking them `pub(crate)`, and it works fine. + /** * A generator for a wrapper tuple newtype over a map shape's symbol type. * diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 3c8313dc95..bf19986329 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.Attribute @@ -18,12 +17,11 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.letIf @@ -35,15 +33,24 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase // TODO Docs class UnconstrainedUnionGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: UnionShape ) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } @@ -154,10 +161,10 @@ class UnconstrainedUnionGenerator( ) { if (member.targetCanReachConstrainedShape(model, symbolProvider)) { val targetShape = model.expectShape(member.target) - val resolveToNonPublicConstrainedType = - !targetShape.isDirectlyConstrained(symbolProvider) && + val resolveToNonPublicConstrainedType = !publicConstrainedTypes || + (!targetShape.isDirectlyConstrained(symbolProvider) && !targetShape.isStructureShape && - !targetShape.isUnionShape + !targetShape.isUnionShape) val hasBox = member.hasTrait() if (hasBox) { @@ -165,15 +172,21 @@ class UnconstrainedUnionGenerator( } if (resolveToNonPublicConstrainedType) { + val constrainedSymbol = + if (!publicConstrainedTypes && targetShape.isDirectlyConstrained(symbolProvider)) { + codegenContext.constrainedShapeSymbolProvider.toSymbol(targetShape) + } else { + pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) + } rustTemplate( """ - let constrained: #{PubCrateConstrainedShapeSymbol} = unconstrained + let constrained: #{ConstrainedSymbol} = unconstrained .try_into() ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } .map_err(Self::Error::${ConstraintViolation(member).name()})?; constrained.into() """, - "PubCrateConstrainedShapeSymbol" to pubCrateConstrainedShapeSymbolProvider.toSymbol(targetShape) + "ConstrainedSymbol" to constrainedSymbol ) } else { rust( From 1e7c975267ad67f5cb422a2964365ba9a441e7de Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 30 Aug 2022 17:33:09 +0200 Subject: [PATCH 150/255] save work --- codegen-server-test/model/constraints.smithy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 22e52ab32f..ca4fd9a4cf 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -209,8 +209,7 @@ structure ConA { mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, constrainedUnion: ConstrainedUnion, -// enumString: EnumString, - // TODO Uncomment enumString from union when above line is uncommented. + enumString: EnumString, // listOfLengthString: ListOfLengthString, @@ -265,7 +264,7 @@ string MediaTypeLengthString /// A union with constrained members. union ConstrainedUnion { - //enumString: EnumString, + enumString: EnumString, lengthString: LengthString, constrainedStructure: ConB, From 3692e6770d454b07acd91d075adbadb2b0d4e592 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 30 Aug 2022 19:03:07 +0200 Subject: [PATCH 151/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation->ConA->Until EnumString uncommented --- .../server/smithy/ServerCodegenVisitor.kt | 28 ++------- .../MapConstraintViolationGenerator.kt | 10 +++- .../smithy/generators/ServerEnumGenerator.kt | 57 +++++++++++-------- .../UnconstrainedCollectionGenerator.kt | 8 ++- .../generators/UnconstrainedUnionGenerator.kt | 8 ++- 5 files changed, 60 insertions(+), 51 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 2d517fc072..790c5b582c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -349,11 +349,7 @@ open class ServerCodegenVisitor( if (isDirectlyConstrained || renderUnconstrainedMap) { rustCrate.withModule(ModelsModule) { modelsModuleWriter -> - MapConstraintViolationGenerator( - codegenContext, - modelsModuleWriter, - shape - ).render() + MapConstraintViolationGenerator(codegenContext, modelsModuleWriter, shape).render() } } } @@ -367,20 +363,8 @@ open class ServerCodegenVisitor( shape.getTrait()?.also { enum -> logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> - ServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - shape, - enum - ).render() - ConstrainedTraitForEnumGenerator( - model, - symbolProvider, - writer, - shape - ).render() + ServerEnumGenerator(codegenContext, writer, shape, enum).render() + ConstrainedTraitForEnumGenerator(model, symbolProvider, writer, shape).render() } } @@ -396,11 +380,7 @@ open class ServerCodegenVisitor( } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> - ConstrainedStringGenerator( - codegenContext, - writer, - shape - ).render() + ConstrainedStringGenerator(codegenContext, writer, shape).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 33a3bb50a3..b68398e735 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -8,7 +8,9 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext @@ -50,8 +52,14 @@ class MapConstraintViolationGenerator( }, ).toTypedArray() + val constraintViolationVisibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } modelsModuleWriter.withModule( - constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), + RustMetadata(visibility = constraintViolationVisibility) ) { // TODO We should really have two `ConstraintViolation` types here. One will just have variants for each // constraint trait on the map shape, for use by the user. The other one will have variants if the shape's diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 1e030fee11..b7a359b839 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -4,65 +4,74 @@ */ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.util.dq -import software.amazon.smithy.rust.codegen.util.toSnakeCase open class ServerEnumGenerator( - model: Model, - symbolProvider: RustSymbolProvider, - constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: StringShape, enumTrait: EnumTrait, -) : EnumGenerator(model, symbolProvider, writer, shape, enumTrait) { +) : EnumGenerator(codegenContext.model, codegenContext.symbolProvider, writer, shape, enumTrait) { override var target: CodegenTarget = CodegenTarget.SERVER - private val unknownVariantSymbol = constraintViolationSymbolProvider.toSymbol(shape) + + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with (codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + private val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) + private val constraintViolationName = constraintViolationSymbol.name override fun renderFromForStr() { - // TODO Shouldn't `ConstraintViolation` be unknownVariantSymbol.name? - // TODO We could put all the impl blocks / trait impls inside the mod. - writer.rust( - """ - pub mod ${enumName.toSnakeCase()} { + writer.withModule( + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + ) { + rustTemplate( + """ ##[derive(Debug, PartialEq)] - pub struct ConstraintViolation(pub(crate) String); - } - """ - ) + pub struct $constraintViolationName(pub(crate) #{String}); + """, + "String" to RuntimeType.String, + ) + } writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { - rust("type Error = #T;", unknownVariantSymbol) + rust("type Error = #T;", constraintViolationSymbol) rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { rustBlock("match s") { sortedMembers.forEach { member -> rust("${member.value.dq()} => Ok($enumName::${member.derivedName()}),") } - rust("_ => Err(#T(s.to_owned()))", unknownVariantSymbol) + rust("_ => Err(#T(s.to_owned()))", constraintViolationSymbol) } } } writer.rustTemplate( """ - impl #{TryFrom} for $enumName { + impl #{TryFrom}<#{String}> for $enumName { type Error = #{UnknownVariantSymbol}; - fn try_from(s: String) -> std::result::Result>::Error> { + fn try_from(s: #{String}) -> std::result::Result>::Error> { s.as_str().try_into() } } """, + "String" to RuntimeType.String, "TryFrom" to RuntimeType.TryFrom, - "UnknownVariantSymbol" to unknownVariantSymbol + "UnknownVariantSymbol" to constraintViolationSymbol ) } @@ -76,7 +85,7 @@ open class ServerEnumGenerator( } } """, - "UnknownVariantSymbol" to unknownVariantSymbol + "UnknownVariantSymbol" to constraintViolationSymbol ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 3cec3b99a9..d59cfa33cb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -86,8 +86,14 @@ class UnconstrainedCollectionGenerator( ) } + val constraintViolationVisibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } modelsModuleWriter.withModule( - constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), + RustMetadata(visibility = constraintViolationVisibility) ) { rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index bf19986329..269754b3ee 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -114,8 +114,14 @@ class UnconstrainedUnionGenerator( "UnconstrainedSymbol" to symbol ) + val constraintViolationVisibility = if (publicConstrainedTypes) { + Visibility.PUBLIC + } else { + Visibility.PUBCRATE + } modelsModuleWriter.withModule( - constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), + RustMetadata(visibility = constraintViolationVisibility) ) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(this) rustBlock("pub enum $constraintViolationName") { From 466b03cfbef0c5cb086d67f4b9e65d3463b7156f Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 31 Aug 2022 14:56:21 +0200 Subject: [PATCH 152/255] constraints,constraints_without_public_constrained_types work: ConstrainedShapesOperation done --- codegen-server-test/model/constraints.smithy | 6 +- .../server/smithy/ServerCodegenVisitor.kt | 8 +- .../generators/ConstrainedMapGenerator.kt | 6 +- .../PubCrateConstrainedCollectionGenerator.kt | 117 +++++++++++------- .../PubCrateConstrainedMapGenerator.kt | 104 ++++++++++------ .../generators/UnconstrainedMapGenerator.kt | 4 +- .../smithy/rust/codegen/smithy/Constraints.kt | 4 +- .../PubCrateConstrainedShapeSymbolProvider.kt | 2 +- .../serialize/JsonSerializerGenerator.kt | 2 +- 9 files changed, 152 insertions(+), 101 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index ca4fd9a4cf..766750923b 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -211,9 +211,9 @@ structure ConA { constrainedUnion: ConstrainedUnion, enumString: EnumString, -// listOfLengthString: ListOfLengthString, - -// setOfLengthString: SetOfLengthString, + listOfLengthString: ListOfLengthString, + setOfLengthString: SetOfLengthString, + mapOfLengthString: MapOfLengthString, } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 790c5b582c..fbfa149d9c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -291,9 +291,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a constrained type for collection shape $shape") rustCrate.withModule(constrainedModule) { writer -> PubCrateConstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext, pubCrateConstrainedShapeSymbolProvider, writer, shape @@ -324,9 +322,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a constrained type for map $shape") rustCrate.withModule(constrainedModule) { writer -> PubCrateConstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext, pubCrateConstrainedShapeSymbolProvider, writer, shape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 0292d2ed71..67eb3b6a3d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -37,9 +37,9 @@ class ConstrainedMapGenerator( val shape: MapShape, private val unconstrainedSymbol: Symbol? = null, ) { - val model = codegenContext.model - val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider - val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val model = codegenContext.model + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = with (codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index b3205449a9..76f146b8ea 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -5,19 +5,18 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained +import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyConstrained /** * A generator for a wrapper tuple newtype over a collection shape's symbol @@ -36,42 +35,40 @@ import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained * instead. */ class PubCrateConstrainedCollectionGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: CollectionShape ) { + private val model = codegenContext.model + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + private val symbolProvider = codegenContext.symbolProvider + fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) val symbol = symbolProvider.toSymbol(shape) val constrainedSymbol = pubCrateConstrainedShapeSymbolProvider.toSymbol(shape) + val unconstrainedSymbol = unconstrainedShapeSymbolProvider.toSymbol(shape) val module = constrainedSymbol.namespace.split(constrainedSymbol.namespaceDelimiter).last() val name = constrainedSymbol.name val innerShape = model.expectShape(shape.member.target) - val innerConstrainedSymbol = if (innerShape.isTransitivelyConstrained(model, symbolProvider)) { + val innerConstrainedSymbol = if (innerShape.isTransitivelyButNotDirectlyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(innerShape) } else { - symbolProvider.toSymbol(innerShape) + constrainedShapeSymbolProvider.toSymbol(innerShape) } - // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, - // then its corresponding constrained type is the `pub(crate)` wrapper tuple type, which needs converting into - // the public type the user is exposed to. The two types are isomorphic, and we can convert between them using - // `From`. So we track this particular case here in order to iterate over the list's members and convert - // each of them. - // - // Note that we could add the iteration code unconditionally and it would still be correct, but the `into()` calls - // would be useless. Clippy flags this as [`useless_conversion`]. We could deactivate the lint, but it's probably - // best that we just don't emit a useless iteration, lest the compiler not optimize it away, and to make the - // generated code a little bit simpler. - // - // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. - val innerNeedsConstraining = - !innerShape.isDirectlyConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape.isMapShape) + val codegenScope = arrayOf( + "InnerConstrainedSymbol" to innerConstrainedSymbol, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, + "Symbol" to symbol, + "From" to RuntimeType.From, + ) writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( @@ -82,32 +79,62 @@ class PubCrateConstrainedCollectionGenerator( impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; } + """, + *codegenScope + ) + + if (publicConstrainedTypes) { + // If the target member shape is itself _not_ directly constrained, and is an aggregate non-Structure shape, + // then its corresponding constrained type is the `pub(crate)` wrapper tuple type, which needs converting into + // the public type the user is exposed to. The two types are isomorphic, and we can convert between them using + // `From`. So we track this particular case here in order to iterate over the list's members and convert + // each of them. + // + // Note that we could add the iteration code unconditionally and it would still be correct, but the `into()` calls + // would be useless. Clippy flags this as [`useless_conversion`]. We could deactivate the lint, but it's probably + // best that we just don't emit a useless iteration, lest the compiler not optimize it away, and to make the + // generated code a little bit simpler. + // + // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. + val innerNeedsConstraining = + !innerShape.isDirectlyConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape is MapShape) - impl From<#{Symbol}> for $name { - fn from(v: #{Symbol}) -> Self { - ${ if (innerNeedsConstraining) { - "Self(v.into_iter().map(|item| item.into()).collect())" - } else { - "Self(v)" - } } + rustTemplate( + """ + impl #{From}<#{Symbol}> for $name { + fn from(v: #{Symbol}) -> Self { + ${ if (innerNeedsConstraining) { + "Self(v.into_iter().map(|item| item.into()).collect())" + } else { + "Self(v)" + } } + } } - } - impl From<$name> for #{Symbol} { - fn from(v: $name) -> Self { - ${ if (innerNeedsConstraining) { - "v.0.into_iter().map(|item| item.into()).collect()" - } else { - "v.0" - } } + impl #{From}<$name> for #{Symbol} { + fn from(v: $name) -> Self { + ${ if (innerNeedsConstraining) { + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } + } } - } - """, - "InnerConstrainedSymbol" to innerConstrainedSymbol, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "UnconstrainedSymbol" to unconstrainedSymbol, - "Symbol" to symbol, - ) + """, + *codegenScope + ) + } else { + rustTemplate( + """ + impl #{From}<$name> for #{Symbol} { + fn from(v: $name) -> Self { + v.0.into_iter().map(|item| item.into()).collect() + } + } + """, + *codegenScope + ) + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index d979d20620..ad75357dcd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape @@ -15,16 +14,18 @@ import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained +import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyConstrained // TODO I'm looking at this class and `ConstrainedMapGenerator` and cannot help but think that we could dispense with // the stuff `PubCrate*` (i.e. the entire `constrained module), by just generating the `Constrained*` shapes and // marking them `pub(crate)`. After all, when `publicConstrainedTypes` is `false`, we're still generating the // constrained types and using them in the deser path, but we're marking them `pub(crate)`, and it works fine. +// Maybe this approach is better, since this is less code than the public constrained types' generators, and it +// makes the distinction between what is directly constrained vs what is transitively but not directly constrained +// clearer. /** * A generator for a wrapper tuple newtype over a map shape's symbol type. @@ -41,13 +42,17 @@ import software.amazon.smithy.rust.codegen.smithy.isTransitivelyConstrained * instead. */ class PubCrateConstrainedMapGenerator( - val model: Model, - val symbolProvider: RustSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: MapShape ) { + private val model = codegenContext.model + private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + private val symbolProvider = codegenContext.symbolProvider + fun render() { check(shape.canReachConstrainedShape(model, symbolProvider)) @@ -58,18 +63,21 @@ class PubCrateConstrainedMapGenerator( val name = constrainedSymbol.name val keyShape = model.expectShape(shape.key.target, StringShape::class.java) val valueShape = model.expectShape(shape.value.target) - val keySymbol = symbolProvider.toSymbol(keyShape) - val valueSymbol = if (valueShape.isTransitivelyConstrained(model, symbolProvider)) { + val keySymbol = constrainedShapeSymbolProvider.toSymbol(keyShape) + val valueSymbol = if (valueShape.isTransitivelyButNotDirectlyConstrained(model, symbolProvider)) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { - symbolProvider.toSymbol(valueShape) + constrainedShapeSymbolProvider.toSymbol(valueShape) } - // Unless the map holds an aggregate shape as its value shape whose symbol's type is _not_ `pub(crate)`, the - // `.into()` calls are useless. - // See the comment in [ConstrainedCollectionShape] for a more detailed explanation. - val innerNeedsConstraining = - !valueShape.isDirectlyConstrained(symbolProvider) && (valueShape is CollectionShape || valueShape.isMapShape) + val codegenScope = arrayOf( + "KeySymbol" to keySymbol, + "ValueSymbol" to valueSymbol, + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "UnconstrainedSymbol" to unconstrainedSymbol, + "Symbol" to symbol, + "From" to RuntimeType.From, + ) writer.withModule(module, RustMetadata(visibility = Visibility.PUBCRATE)) { rustTemplate( @@ -80,33 +88,53 @@ class PubCrateConstrainedMapGenerator( impl #{ConstrainedTrait} for $name { type Unconstrained = #{UnconstrainedSymbol}; } + """, + *codegenScope + ) - impl From<#{Symbol}> for $name { - fn from(v: #{Symbol}) -> Self { - ${ if (innerNeedsConstraining) { - "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" - } else { - "Self(v)" - } } + if (publicConstrainedTypes) { + // Unless the map holds an aggregate shape as its value shape whose symbol's type is _not_ `pub(crate)`, the + // `.into()` calls are useless. + // See the comment in [ConstrainedCollectionShape] for a more detailed explanation. + val innerNeedsConstraining = + !valueShape.isDirectlyConstrained(symbolProvider) && (valueShape is CollectionShape || valueShape is MapShape) + + rustTemplate( + """ + impl #{From}<#{Symbol}> for $name { + fn from(v: #{Symbol}) -> Self { + ${ if (innerNeedsConstraining) { + "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" + } else { + "Self(v)" + } } + } } - } - impl From<$name> for #{Symbol} { - fn from(v: $name) -> Self { - ${ if (innerNeedsConstraining) { - "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" - } else { - "v.0" - } } + impl #{From}<$name> for #{Symbol} { + fn from(v: $name) -> Self { + ${ if (innerNeedsConstraining) { + "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" + } else { + "v.0" + } } + } } - } - """, - "KeySymbol" to keySymbol, - "ValueSymbol" to valueSymbol, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), - "UnconstrainedSymbol" to unconstrainedSymbol, - "Symbol" to symbol, - ) + """, + *codegenScope + ) + } else { + rustTemplate( + """ + impl #{From}<$name> for #{Symbol} { + fn from(v: $name) -> Self { + v.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect() + } + } + """, + *codegenScope + ) + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 200950a377..6bae1333e5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -100,7 +100,7 @@ class UnconstrainedMapGenerator( rustTemplate( """ - let res: Result, Self::Error> = value.0 + let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { ${if (isKeyConstrained(keyShape, symbolProvider)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} @@ -110,7 +110,7 @@ class UnconstrainedMapGenerator( .collect(); let hm = res?; """, - "KeySymbol" to symbolProvider.toSymbol(keyShape), + "ConstrainedKeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape), "ConstrainedValueSymbol" to constrainedValueSymbol ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 39ade7f766..8ffebcd6c0 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -65,7 +65,7 @@ fun MemberShape.requiresNewtype() = fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider) = this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) -fun Shape.isTransitivelyConstrained(model: Model, symbolProvider: SymbolProvider) = +fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider) = !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = @@ -76,4 +76,4 @@ fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) this.targetCanReachConstrainedShape(model, symbolProvider) } else { Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - } \ No newline at end of file + } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 93928ab03c..db6fdcd30d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -76,7 +76,7 @@ class PubCrateConstrainedShapeSymbolProvider( "This symbol provider was called with $shape. However, it can only be called with a shape that is transitively constrained." override fun toSymbol(shape: Shape): Symbol { - require(shape.isTransitivelyConstrained(model, base)) { errorMessage(shape) } + require(shape.isTransitivelyButNotDirectlyConstrained(model, base)) { errorMessage(shape) } return when (shape) { is CollectionShape, is MapShape -> { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index 8db03d8f35..203c1cf3a4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -431,7 +431,7 @@ class JsonSerializerGenerator( val keyTarget = model.expectShape(context.shape.key.target) val workingWithPublicConstrainedWrapperTupleType = codegenTarget == CodegenTarget.SERVER && keyTarget.hasPublicConstrainedWrapperTupleType(model) - val keyExpression = if (workingWithPublicConstrainedWrapperTupleType) { + val keyExpression = if (publicConstrainedTypes && workingWithPublicConstrainedWrapperTupleType) { "$keyName.0.as_str()" } else if (keyTarget.hasTrait()) { "$keyName.as_str()" From 4e62c132b178323ed9d1f29ef872fd4bb5e903fa Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 13:31:38 +0200 Subject: [PATCH 153/255] constraints,constraints_without_public_constrained_types work: ConstrainedHttpBoundShapesOperationInputOutput->until enumStringLabel uncommented --- codegen-server-test/model/constraints.smithy | 56 +++++++++---------- .../server/smithy/ServerCodegenVisitor.kt | 24 ++++---- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 766750923b..1d1633bc76 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -10,7 +10,7 @@ use aws.protocols#restJson1 service ConstraintsService { operations: [ ConstrainedShapesOperation, -// ConstrainedHttpBoundShapesOperation, + ConstrainedHttpBoundShapesOperation, // ConstrainedRecursiveShapesOperation, // // `httpQueryParams` and `httpPrefixHeaders` are structurually // // exclusive, so we need one operation per target shape type @@ -108,44 +108,44 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpLabel enumStringLabel: EnumString, - // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working - // @required - @httpPrefixHeaders("X-Prefix-Headers-") - lengthStringHeaderMap: MapOfLengthString, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working +// // @required +// @httpPrefixHeaders("X-Prefix-Headers-") +// lengthStringHeaderMap: MapOfLengthString, - @httpHeader("X-Length") - lengthStringHeader: LengthString, +// @httpHeader("X-Length") +// lengthStringHeader: LengthString, - // @httpHeader("X-Length-MediaType") - // lengthStringHeaderWithMediaType: MediaTypeLengthString, +// // @httpHeader("X-Length-MediaType") +// // lengthStringHeaderWithMediaType: MediaTypeLengthString, - @httpHeader("X-Length-Set") - lengthStringSetHeader: SetOfLengthString, +// @httpHeader("X-Length-Set") +// lengthStringSetHeader: SetOfLengthString, - @httpHeader("X-Length-List") - lengthStringListHeader: ListOfLengthString, +// @httpHeader("X-Length-List") +// lengthStringListHeader: ListOfLengthString, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // @httpHeader("X-Enum") - //enumStringHeader: EnumString, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) +// // @httpHeader("X-Enum") +// //enumStringHeader: EnumString, - // @httpHeader("X-Enum-List") - // enumStringListHeader: ListOfEnumString, +// // @httpHeader("X-Enum-List") +// // enumStringListHeader: ListOfEnumString, - @httpQuery("lengthString") - lengthStringQuery: LengthString, +// @httpQuery("lengthString") +// lengthStringQuery: LengthString, - @httpQuery("enumString") - enumStringQuery: EnumString, +// @httpQuery("enumString") +// enumStringQuery: EnumString, - @httpQuery("lengthStringList") - lengthStringListQuery: ListOfLengthString, +// @httpQuery("lengthStringList") +// lengthStringListQuery: ListOfLengthString, - @httpQuery("lengthStringSet") - lengthStringSetQuery: SetOfLengthString, +// @httpQuery("lengthStringSet") +// lengthStringSetQuery: SetOfLengthString, - @httpQuery("enumStringList") - enumStringListQuery: ListOfEnumString, +// @httpQuery("enumStringList") +// enumStringListQuery: ListOfEnumString, } structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index fbfa149d9c..aa28dd8ead 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -156,8 +156,8 @@ open class ServerCodegenVisitor( rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) - val walker = Walker(model) val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) } + val walker = Walker(model) shapesReachableFromOperationInputs = inputShapes.flatMap { walker.walkShapes(it) }.toSet() } @@ -233,15 +233,19 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val serverBuilderGenerator = ServerBuilderGenerator( - codegenContext, - shape, - if (shapesReachableFromOperationInputs.contains(shape)) pubCrateConstrainedShapeSymbolProvider else null - ) - serverBuilderGenerator.render(writer) - if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - writer.implBlock(shape, symbolProvider) { - serverBuilderGenerator.renderConvenienceMethod(this) + + if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shapesReachableFromOperationInputs.contains(shape)) { + val serverBuilderGenerator = ServerBuilderGenerator( + codegenContext, + shape, + if (shapesReachableFromOperationInputs.contains(shape)) pubCrateConstrainedShapeSymbolProvider else null + ) + serverBuilderGenerator.render(writer) + + if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { + writer.implBlock(shape, symbolProvider) { + serverBuilderGenerator.renderConvenienceMethod(this) + } } } From 39c457d599c515c1ad52ebcf74d3d40cfe30e356 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 13:48:41 +0200 Subject: [PATCH 154/255] constraints,constraints_without_public_constrained_types work: ConstrainedHttpBoundShapesOperationInputOutput->until lengthStringHeaderMap uncommented --- codegen-server-test/model/constraints.smithy | 8 ++++---- .../generators/http/HttpBindingGenerator.kt | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 1d1633bc76..2ae91b63bb 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -108,10 +108,10 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpLabel enumStringLabel: EnumString, -// // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working -// // @required -// @httpPrefixHeaders("X-Prefix-Headers-") -// lengthStringHeaderMap: MapOfLengthString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1394) `@required` not working + // @required + @httpPrefixHeaders("X-Prefix-Headers-") + lengthStringHeaderMap: MapOfLengthString, // @httpHeader("X-Length") // lengthStringHeader: LengthString, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index d1bcadf892..c18a28d905 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -38,6 +38,7 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError @@ -92,7 +93,7 @@ enum class HttpMessageType { */ class HttpBindingGenerator( private val protocol: Protocol, - coreCodegenContext: CoreCodegenContext, + private val coreCodegenContext: CoreCodegenContext, private val symbolProvider: RustSymbolProvider, private val operationShape: OperationShape ) { @@ -604,12 +605,9 @@ class HttpBindingGenerator( "$func(&$targetName)" } else { // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member traits are not - // supported yet. Note that here, counterintuitively, in case we're rendering a header for a - // collection, `member` is still referring to the structure member shape where the `httpHeader` or - // `httpPrefixHeaders` trait was found, and _not_ to the collection member shape on which we'd have - // to check for constraint trait precedence. So `member.hasPublicConstrainedWrapperTupleType()` is - // _not_ what we want. - quoteValue("AsRef::::as_ref(${if (workingWithPublicConstrainedWrapperTupleType(target)) "&$targetName.0" else targetName})") + // supported yet. + val expr = if (workingWithPublicConstrainedWrapperTupleType(target)) "&$targetName.0" else targetName + quoteValue("AsRef::::as_ref($expr)") } } target.isTimestampShape -> { @@ -629,5 +627,7 @@ class HttpBindingGenerator( } private fun workingWithPublicConstrainedWrapperTupleType(shape: Shape) = - codegenTarget == CodegenTarget.SERVER && shape.hasPublicConstrainedWrapperTupleType(model) + codegenTarget == CodegenTarget.SERVER && + (coreCodegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes && + shape.hasPublicConstrainedWrapperTupleType(model) } From 181769b86d524e1e8ac780020c20efb22f071995 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 14:10:15 +0200 Subject: [PATCH 155/255] constraints,constraints_without_public_constrained_types work: ConstrainedHttpBoundShapesOperationInputOutput->mostly everything uncommented --- codegen-server-test/model/constraints.smithy | 46 ++++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 2ae91b63bb..e4d199fce7 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -113,39 +113,39 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { @httpPrefixHeaders("X-Prefix-Headers-") lengthStringHeaderMap: MapOfLengthString, -// @httpHeader("X-Length") -// lengthStringHeader: LengthString, + @httpHeader("X-Length") + lengthStringHeader: LengthString, -// // @httpHeader("X-Length-MediaType") -// // lengthStringHeaderWithMediaType: MediaTypeLengthString, + // @httpHeader("X-Length-MediaType") + // lengthStringHeaderWithMediaType: MediaTypeLengthString, -// @httpHeader("X-Length-Set") -// lengthStringSetHeader: SetOfLengthString, + @httpHeader("X-Length-Set") + lengthStringSetHeader: SetOfLengthString, -// @httpHeader("X-Length-List") -// lengthStringListHeader: ListOfLengthString, + @httpHeader("X-Length-List") + lengthStringListHeader: ListOfLengthString, -// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) -// // @httpHeader("X-Enum") -// //enumStringHeader: EnumString, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + // @httpHeader("X-Enum") + //enumStringHeader: EnumString, -// // @httpHeader("X-Enum-List") -// // enumStringListHeader: ListOfEnumString, + // @httpHeader("X-Enum-List") + // enumStringListHeader: ListOfEnumString, -// @httpQuery("lengthString") -// lengthStringQuery: LengthString, + @httpQuery("lengthString") + lengthStringQuery: LengthString, -// @httpQuery("enumString") -// enumStringQuery: EnumString, + @httpQuery("enumString") + enumStringQuery: EnumString, -// @httpQuery("lengthStringList") -// lengthStringListQuery: ListOfLengthString, + @httpQuery("lengthStringList") + lengthStringListQuery: ListOfLengthString, -// @httpQuery("lengthStringSet") -// lengthStringSetQuery: SetOfLengthString, + @httpQuery("lengthStringSet") + lengthStringSetQuery: SetOfLengthString, -// @httpQuery("enumStringList") -// enumStringListQuery: ListOfEnumString, + @httpQuery("enumStringList") + enumStringListQuery: ListOfEnumString, } structure HttpPrefixHeadersTargetingLengthMapOperationInputOutput { From 3c9fb915fef5d07423ae25959a5f2b20500e2a99 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 14:29:56 +0200 Subject: [PATCH 156/255] constraints,constraints_without_public_constrained_types work: ConstrainedRecursiveShapesOperation uncommented --- codegen-server-test/model/constraints.smithy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index e4d199fce7..f031d9c81e 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -11,7 +11,7 @@ service ConstraintsService { operations: [ ConstrainedShapesOperation, ConstrainedHttpBoundShapesOperation, -// ConstrainedRecursiveShapesOperation, + ConstrainedRecursiveShapesOperation, // // `httpQueryParams` and `httpPrefixHeaders` are structurually // // exclusive, so we need one operation per target shape type // // combination. From d8a15912d1add639ea030bc413c0368bfe5b36fe Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 14:58:10 +0200 Subject: [PATCH 157/255] constraints,constraints_without_public_constrained_types work: all operations uncommented --- codegen-server-test/model/constraints.smithy | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index f031d9c81e..0150a70533 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -12,18 +12,18 @@ service ConstraintsService { ConstrainedShapesOperation, ConstrainedHttpBoundShapesOperation, ConstrainedRecursiveShapesOperation, -// // `httpQueryParams` and `httpPrefixHeaders` are structurually -// // exclusive, so we need one operation per target shape type -// // combination. -// QueryParamsTargetingLengthMapOperation, -// QueryParamsTargetingMapOfLengthStringOperation, -// QueryParamsTargetingMapOfEnumStringOperation, -// QueryParamsTargetingMapOfListOfLengthStringOperation, -// QueryParamsTargetingMapOfSetOfLengthStringOperation, -// QueryParamsTargetingMapOfListOfEnumStringOperation, -// HttpPrefixHeadersTargetingLengthMapOperation, -// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) -// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfEnumStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, + QueryParamsTargetingMapOfListOfEnumStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + // HttpPrefixHeadersTargetingMapOfEnumStringOperation, ], } From f0bccb39a93e12af1200e0bffeb6a2bec99ff455 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 1 Sep 2022 19:14:42 +0200 Subject: [PATCH 158/255] constraints,constraints_without_public_constrained_types work: blob streams --- codegen-server-test/model/constraints.smithy | 16 ++++++++++++++++ .../smithy/generators/ServerBuilderGenerator.kt | 12 ++++++++++-- .../smithy/rust/codegen/smithy/Constraints.kt | 3 ++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 0150a70533..cf78ee4df7 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -24,6 +24,8 @@ service ConstraintsService { HttpPrefixHeadersTargetingLengthMapOperation, // TODO(https://github.com/awslabs/smithy-rs/issues/1431) // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + + BlobStreamingOperation, ], } @@ -94,6 +96,12 @@ operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, } +@http(uri: "/blob-streaming-operation", method: "GET") +operation BlobStreamingOperation { + input: BlobStreamingOperationInputOutput, + output: BlobStreamingOperationInputOutput, +} + structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -188,6 +196,14 @@ structure QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput { mapOfListOfEnumString: MapOfListOfEnumString } +structure BlobStreamingOperationInputOutput { + @httpPayload + streamingBlob: StreamingBlob, +} + +@streaming +blob StreamingBlob + structure ConA { @required conB: ConB, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 2414798f0c..32ee238987 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -377,7 +377,14 @@ class ServerBuilderGenerator( member: MemberShape, ) { val builderMemberSymbol = builderMemberSymbol(member) - val inputType = builderMemberSymbol.rustType().stripOuter().implInto().letIf(member.isOptional) { "Option<$it>" } + val inputType = builderMemberSymbol.rustType().stripOuter().implInto() + .letIf( + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): + // The only reason why this condition can't simply be `member.isOptional` + // is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. + symbolProvider.toSymbol(member).isOptional() + ) { "Option<$it>" } val memberName = symbolProvider.toMemberName(member) writer.documentShape(member, model) @@ -386,7 +393,8 @@ class ServerBuilderGenerator( rust( """ self.$memberName = ${ - if (member.isOptional) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. + if (symbolProvider.toSymbol(member).isOptional()) { "input.map(|v| v.into())" } else { "Some(input.into())" diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 8ffebcd6c0..4583fc38f7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -33,7 +33,8 @@ import software.amazon.smithy.rust.codegen.util.hasTrait */ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { - // TODO(https://github.com/awslabs/smithy-rs/issues/1302): The only reason why the functions in this file have + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): + // The only reason why the functions in this file have // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as // `required`, so we can't use `member.isOptional` here. this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } From b49aa17c6696016d253c002cca1113d677cc32c2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 5 Sep 2022 16:32:50 +0200 Subject: [PATCH 159/255] Add an event streams operation to constraints.smithy --- codegen-server-test/model/constraints.smithy | 84 +++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index cf78ee4df7..8de5a59838 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -9,99 +9,106 @@ use aws.protocols#restJson1 @title("ConstraintsService") service ConstraintsService { operations: [ - ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfEnumStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - QueryParamsTargetingMapOfListOfEnumStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // HttpPrefixHeadersTargetingMapOfEnumStringOperation, - - BlobStreamingOperation, +// ConstrainedShapesOperation, +// ConstrainedHttpBoundShapesOperation, +// ConstrainedRecursiveShapesOperation, +// // `httpQueryParams` and `httpPrefixHeaders` are structurually +// // exclusive, so we need one operation per target shape type +// // combination. +// QueryParamsTargetingLengthMapOperation, +// QueryParamsTargetingMapOfLengthStringOperation, +// QueryParamsTargetingMapOfEnumStringOperation, +// QueryParamsTargetingMapOfListOfLengthStringOperation, +// QueryParamsTargetingMapOfSetOfLengthStringOperation, +// QueryParamsTargetingMapOfListOfEnumStringOperation, +// HttpPrefixHeadersTargetingLengthMapOperation, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) +// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + +// BlobStreamingOperation, + EventStreamsOperation, ], } -@http(uri: "/constrained-shapes-operation", method: "GET") +@http(uri: "/constrained-shapes-operation", method: "POST") operation ConstrainedShapesOperation { input: ConstrainedShapesOperationInputOutput, output: ConstrainedShapesOperationInputOutput, errors: [ErrorWithLengthStringMessage] } -@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "GET") +@http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "POST") operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, } -@http(uri: "/constrained-recursive-shapes-operation", method: "GET") +@http(uri: "/constrained-recursive-shapes-operation", method: "POST") operation ConstrainedRecursiveShapesOperation { input: ConstrainedRecursiveShapesOperationInputOutput, output: ConstrainedRecursiveShapesOperationInputOutput, } -@http(uri: "/query-params-targeting-length-map", method: "GET") +@http(uri: "/query-params-targeting-length-map", method: "POST") operation QueryParamsTargetingLengthMapOperation { input: QueryParamsTargetingLengthMapOperationInputOutput, output: QueryParamsTargetingLengthMapOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-length-string-operation", method: "GET") +@http(uri: "/query-params-targeting-map-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfLengthStringOperation { input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "GET") +@http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "POST") operation QueryParamsTargetingMapOfEnumStringOperation { input: QueryParamsTargetingMapOfEnumStringOperationInputOutput, output: QueryParamsTargetingMapOfEnumStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "GET") +@http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfListOfLengthStringOperation { input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "GET") +@http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfSetOfLengthStringOperation { input: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, } -@http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "GET") +@http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "POST") operation QueryParamsTargetingMapOfListOfEnumStringOperation { input: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, output: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, } -@http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "GET") +@http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "POST") operation HttpPrefixHeadersTargetingLengthMapOperation { input: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, output: HttpPrefixHeadersTargetingLengthMapOperationInputOutput, } -@http(uri: "/http-prefix-headers-targeting-map-of-enum-string-operation", method: "GET") +@http(uri: "/http-prefix-headers-targeting-map-of-enum-string-operation", method: "POST") operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { input: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, } -@http(uri: "/blob-streaming-operation", method: "GET") +@http(uri: "/blob-streaming-operation", method: "POST") operation BlobStreamingOperation { input: BlobStreamingOperationInputOutput, output: BlobStreamingOperationInputOutput, } +@http(uri: "/event-streams-operation", method: "POST") +operation EventStreamsOperation { + input: EventStreamsOperationInputOutput, + output: EventStreamsOperationInputOutput, +} + structure ConstrainedShapesOperationInputOutput { @required conA: ConA, @@ -201,6 +208,25 @@ structure BlobStreamingOperationInputOutput { streamingBlob: StreamingBlob, } +structure EventStreamsOperationInputOutput { + events: Event, +} + +@streaming +union Event { + regularMessage: EventStreamRegularMessage, + errorMessage: EventStreamErrorMessage, +} + +structure EventStreamRegularMessage { + messageContent: String +} + +@error("server") +structure EventStreamErrorMessage { + messageContent: String +} + @streaming blob StreamingBlob From 493cd6ce2369783b2d4f779210afb28c3de5d1e5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 7 Sep 2022 17:17:13 +0200 Subject: [PATCH 160/255] constraints,constraints_without_public_constrained_types work: event streams without constraint traits work; adds new model transformer to tag aggregate shapes reachable from operation input --- codegen-server-test/model/constraints.smithy | 36 ++++++++++--- .../server/smithy/ServerCodegenVisitor.kt | 23 ++++---- .../generators/http/HttpBindingGenerator.kt | 5 +- .../parse/EventStreamUnmarshallerGenerator.kt | 32 +++++------ ...hapeReachableFromOperationInputTagTrait.kt | 16 ++++++ ...ShapesReachableFromOperationInputTagger.kt | 53 +++++++++++++++++++ .../amazon/smithy/rust/codegen/util/Smithy.kt | 16 ++++++ 7 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt create mode 100644 codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 0b4656d7c5..6e89c370f2 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -25,8 +25,10 @@ service ConstraintsService { // TODO(https://github.com/awslabs/smithy-rs/issues/1431) // HttpPrefixHeadersTargetingMapOfEnumStringOperation, -// BlobStreamingOperation, -// EventStreamsOperation, + NonStreamingBlobOperation, + + StreamingBlobOperation, + EventStreamsOperation, ], } @@ -97,10 +99,16 @@ operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, } -@http(uri: "/blob-streaming-operation", method: "POST") -operation BlobStreamingOperation { - input: BlobStreamingOperationInputOutput, - output: BlobStreamingOperationInputOutput, +@http(uri: "/non-streaming-blob-operation", method: "POST") +operation NonStreamingBlobOperation { + input: NonStreamingBlobOperationInputOutput, + output: NonStreamingBlobOperationInputOutput, +} + +@http(uri: "/streaming-blob-operation", method: "POST") +operation StreamingBlobOperation { + input: StreamingBlobOperationInputOutput, + output: StreamingBlobOperationInputOutput, } @http(uri: "/event-streams-operation", method: "POST") @@ -203,7 +211,12 @@ structure QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput { mapOfListOfEnumString: MapOfListOfEnumString } -structure BlobStreamingOperationInputOutput { +structure NonStreamingBlobOperationInputOutput { + @httpPayload + nonStreamingBlob: NonStreamingBlob, +} + +structure StreamingBlobOperationInputOutput { @httpPayload streamingBlob: StreamingBlob, } @@ -221,16 +234,23 @@ union Event { structure EventStreamRegularMessage { messageContent: String + // TODO(https://github.com/awslabs/smithy/issues/1388): Can't add a constraint trait here until the semantics are clarified. + // messageContent: LengthString } @error("server") structure EventStreamErrorMessage { messageContent: String + // TODO(https://github.com/awslabs/smithy/issues/1388): Can't add a constraint trait here until the semantics are clarified. + // messageContent: LengthString } +// TODO(https://github.com/awslabs/smithy/issues/1389): Can't add a constraint trait here until the semantics are clarified. @streaming blob StreamingBlob +blob NonStreamingBlob + structure ConA { @required conB: ConB, @@ -257,6 +277,8 @@ structure ConA { listOfLengthString: ListOfLengthString, setOfLengthString: SetOfLengthString, mapOfLengthString: MapOfLengthString, + + nonStreamingBlob: NonStreamingBlob } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index aa28dd8ead..c7d7a00447 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -57,6 +57,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.smithy.transformers.AggregateShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.smithy.transformers.RecursiveShapeBoxer @@ -64,6 +65,7 @@ import software.amazon.smithy.rust.codegen.smithy.transformers.RemoveEventStream import software.amazon.smithy.rust.codegen.util.CommandFailed import software.amazon.smithy.rust.codegen.util.getTrait import software.amazon.smithy.rust.codegen.util.hasTrait +import software.amazon.smithy.rust.codegen.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.util.runCommand import java.util.logging.Logger @@ -94,7 +96,6 @@ open class ServerCodegenVisitor( RustModule.private(Unconstrained.namespace, "Unconstrained types for constrained shapes.") private val constrainedModule = RustModule.private(Constrained.namespace, "Constrained types for constrained shapes.") - private val shapesReachableFromOperationInputs: Set init { val symbolVisitorConfig = @@ -104,6 +105,7 @@ open class ServerCodegenVisitor( handleRequired = true, handleRustBoxing = true, ) + val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) val (protocol, generator) = @@ -115,6 +117,7 @@ open class ServerCodegenVisitor( ) .protocolFor(context.model, service) protocolGeneratorFactory = generator + model = codegenDecorator.transformModel(service, baseModel) // TODO Can we make it work setting `publicConstrainedTypes` to `false`? symbolProvider = RustCodegenServerPlugin.baseSymbolProvider( @@ -155,10 +158,6 @@ open class ServerCodegenVisitor( rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) - - val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) } - val walker = Walker(model) - shapesReachableFromOperationInputs = inputShapes.flatMap { walker.walkShapes(it) }.toSet() } /** @@ -173,6 +172,8 @@ open class ServerCodegenVisitor( .let(RecursiveShapeBoxer::transform) // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) + // Tag aggregate shapes reachable from operation input. + .let(AggregateShapesReachableFromOperationInputTagger::transform) // Drop unsupported event stream operations from the model .let { RemoveEventStreamOperations.transform(it, settings) } // Normalize event stream operations @@ -234,11 +235,11 @@ open class ServerCodegenVisitor( rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shapesReachableFromOperationInputs.contains(shape)) { + if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) { val serverBuilderGenerator = ServerBuilderGenerator( codegenContext, shape, - if (shapesReachableFromOperationInputs.contains(shape)) pubCrateConstrainedShapeSymbolProvider else null + if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null ) serverBuilderGenerator.render(writer) @@ -249,7 +250,7 @@ open class ServerCodegenVisitor( } } - if (shapesReachableFromOperationInputs.contains(shape)) { + if (shape.isReachableFromOperationInput()) { ServerStructureConstrainedTraitImpl( symbolProvider, codegenContext.settings.codegenConfig.publicConstrainedTypes, @@ -274,7 +275,7 @@ open class ServerCodegenVisitor( override fun setShape(shape: SetShape) = collectionShape(shape) private fun collectionShape(shape: CollectionShape) { - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, symbolProvider ) @@ -306,7 +307,7 @@ open class ServerCodegenVisitor( override fun mapShape(shape: MapShape) { val renderUnconstrainedMap = - shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, symbolProvider ) @@ -398,7 +399,7 @@ open class ServerCodegenVisitor( UnionGenerator(model, symbolProvider, it, shape, renderUnknownVariant = false).render() } - if (shapesReachableFromOperationInputs.contains(shape) && shape.canReachConstrainedShape( + if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, symbolProvider ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index b48a84de75..a6f9881d8b 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -235,12 +235,9 @@ class HttpBindingGenerator( private fun RustWriter.bindEventStreamOutput(operationShape: OperationShape, targetShape: UnionShape) { val unmarshallerConstructorFn = EventStreamUnmarshallerGenerator( protocol, - model, - runtimeConfig, - symbolProvider, + coreCodegenContext, operationShape, targetShape, - codegenTarget ).render() rustTemplate( """ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 2339d01e43..901022be68 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.smithy.protocols.parse import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.ByteShape @@ -31,11 +30,11 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.withBlock -import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol @@ -48,19 +47,20 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase class EventStreamUnmarshallerGenerator( private val protocol: Protocol, - private val model: Model, - runtimeConfig: RuntimeConfig, - private val symbolProvider: RustSymbolProvider, + private val coreCodegenContext: CoreCodegenContext, private val operationShape: OperationShape, private val unionShape: UnionShape, - private val target: CodegenTarget, ) { + private val model = coreCodegenContext.model + private val symbolProvider = coreCodegenContext.symbolProvider + private val codegenTarget = coreCodegenContext.target + private val runtimeConfig = coreCodegenContext.runtimeConfig private val unionSymbol = symbolProvider.toSymbol(unionShape) private val smithyEventStream = CargoDependency.SmithyEventStream(runtimeConfig) - private val errorSymbol = if (target == CodegenTarget.SERVER && unionShape.eventStreamErrors().isEmpty()) { + private val errorSymbol = if (codegenTarget == CodegenTarget.SERVER && unionShape.eventStreamErrors().isEmpty()) { RuntimeType("MessageStreamError", smithyEventStream, "aws_smithy_http::event_stream").toSymbol() } else { - unionShape.eventStreamErrorSymbol(model, symbolProvider, target).toSymbol() + unionShape.eventStreamErrorSymbol(model, symbolProvider, codegenTarget).toSymbol() } private val eventStreamSerdeModule = RustModule.private("event_stream_serde") private val codegenScope = arrayOf( @@ -149,7 +149,7 @@ class EventStreamUnmarshallerGenerator( } } rustBlock("_unknown_variant => ") { - when (target.renderUnknownVariant()) { + when (codegenTarget.renderUnknownVariant()) { true -> rustTemplate( "Ok(#{UnmarshalledMessage}::Event(#{Output}::${UnionGenerator.UnknownVariantName}))", "Output" to unionSymbol, @@ -191,7 +191,7 @@ class EventStreamUnmarshallerGenerator( ) } else -> { - rust("let mut builder = #T::builder();", symbolProvider.toSymbol(unionStruct)) + rust("let mut builder = #T::default();", unionStruct.builderSymbol(coreCodegenContext, symbolProvider)) val payloadMember = unionStruct.members().firstOrNull { it.hasTrait() } if (payloadMember != null) { renderUnmarshallEventPayload(payloadMember) @@ -297,7 +297,7 @@ class EventStreamUnmarshallerGenerator( } private fun RustWriter.renderUnmarshallError() { - when (target) { + when (codegenTarget) { CodegenTarget.CLIENT -> { rustTemplate( """ @@ -326,12 +326,12 @@ class EventStreamUnmarshallerGenerator( rustBlock("${member.memberName.dq()} $matchOperator ") { // TODO(EventStream): Errors on the operation can be disjoint with errors in the union, // so we need to generate a new top-level Error type for each event stream union. - when (target) { + when (codegenTarget) { CodegenTarget.CLIENT -> { val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) if (parser != null) { - rust("let mut builder = #T::builder();", symbolProvider.toSymbol(target)) + rust("let mut builder = #T::default();", target.builderSymbol(coreCodegenContext, symbolProvider)) rustTemplate( """ builder = #{parser}(&message.payload()[..], builder) @@ -354,7 +354,7 @@ class EventStreamUnmarshallerGenerator( val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) val mut = if (parser != null) { " mut" } else { "" } - rust("let$mut builder = #T::builder();", symbolProvider.toSymbol(target)) + rust("let$mut builder = #T::default();", target.builderSymbol(coreCodegenContext, symbolProvider)) if (parser != null) { rustTemplate( """ @@ -387,7 +387,7 @@ class EventStreamUnmarshallerGenerator( rust("}") } } - when (target) { + when (codegenTarget) { CodegenTarget.CLIENT -> { rustTemplate("Ok(#{UnmarshalledMessage}::Error(#{OpError}::generic(generic)))", *codegenScope) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt new file mode 100644 index 0000000000..58a76eb4e2 --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt @@ -0,0 +1,16 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.smithy.traits + +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.AnnotationTrait + +class SyntheticAggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { + companion object { + val ID = ShapeId.from("smithy.api.internal#syntheticStructureReachableFromOperationInputTag") + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt new file mode 100644 index 0000000000..272093e471 --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.SetShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticAggregateShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.util.UNREACHABLE + +/** + * TODO Docs + */ +object AggregateShapesReachableFromOperationInputTagger { + fun transform(model: Model): Model { + val inputShapes = model.operationShapes.map { + model.expectShape(it.inputShape, StructureShape::class.java) + } + val walker = Walker(model) + val shapesReachableFromOperationInputs = inputShapes + .flatMap { walker.walkShapes(it) } + .toSet() + + return ModelTransformer.create().mapShapes(model) { shape -> + when (shape) { + is StructureShape, is UnionShape, is ListShape, is SetShape, is MapShape -> { + if (shapesReachableFromOperationInputs.contains(shape)) { + val builder = when (shape) { + is StructureShape -> shape.toBuilder() + is UnionShape -> shape.toBuilder() + is ListShape -> shape.toBuilder() + is SetShape -> shape.toBuilder() + is MapShape -> shape.toBuilder() + else -> UNREACHABLE("the `when` is exhaustive") + } + builder.addTrait(SyntheticAggregateShapeReachableFromOperationInputTagTrait()).build() + } else { + shape + } + } + else -> shape + } + } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/util/Smithy.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/util/Smithy.kt index a9b9149ca2..9f0913d3cf 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/util/Smithy.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/util/Smithy.kt @@ -8,16 +8,21 @@ package software.amazon.smithy.rust.codegen.util import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BooleanShape +import software.amazon.smithy.model.shapes.CollectionShape +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.NumberShape 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.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait +import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticAggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticOutputTrait @@ -65,6 +70,17 @@ fun Shape.hasEventStreamMember(model: Model): Boolean { return members().any { it.isEventStream(model) } } +private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { + is StructureShape, is UnionShape, is ListShape, is SetShape, is MapShape -> { + shape.hasTrait() + } else -> PANIC("this method does not support shape type ${shape.type}") +} + +fun StructureShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun CollectionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun UnionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun MapShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) + fun OperationShape.isInputEventStream(model: Model): Boolean { return input.map { id -> model.expectShape(id).hasEventStreamMember(model) }.orElse(false) } From d816c8fcff48845e341d0db8613e96dec660714f Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 7 Sep 2022 18:33:01 +0200 Subject: [PATCH 161/255] rename namespace of constraints.smithy --- codegen-server-test/build.gradle.kts | 4 ++-- codegen-server-test/model/constraints.smithy | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index cf742ba66b..3e0edd6213 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -40,10 +40,10 @@ val allCodegenTests = listOf( CodegenTest("naming_obs_structs#NamingObstacleCourseStructs", "naming_test_structs"), CodegenTest("com.amazonaws.simple#SimpleService", "simple"), CodegenTest( - "com.amazonaws.simple#ConstraintsService", "constraints_without_public_constrained_types", + "com.amazonaws.constraints#ConstraintsService", "constraints_without_public_constrained_types", extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, ), - CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), + CodegenTest("com.amazonaws.constraints#ConstraintsService", "constraints"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras"), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), diff --git a/codegen-server-test/model/constraints.smithy b/codegen-server-test/model/constraints.smithy index 6e89c370f2..d4ac551cc2 100644 --- a/codegen-server-test/model/constraints.smithy +++ b/codegen-server-test/model/constraints.smithy @@ -1,6 +1,6 @@ $version: "1.0" -namespace com.amazonaws.simple +namespace com.amazonaws.constraints use aws.protocols#restJson1 From 77acd23c52ea39d8ba020c6b9da5cbf97a296ae0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 7 Sep 2022 19:55:37 +0200 Subject: [PATCH 162/255] pokemon service: server and client SDKs work --- codegen-server-test/model/pokemon.smithy | 1 - .../server/smithy/ServerCodegenVisitor.kt | 5 ++ .../parse/EventStreamUnmarshallerGenerator.kt | 65 ++++++++++--------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/codegen-server-test/model/pokemon.smithy b/codegen-server-test/model/pokemon.smithy index 025636bce4..f53a34573f 100644 --- a/codegen-server-test/model/pokemon.smithy +++ b/codegen-server-test/model/pokemon.smithy @@ -100,7 +100,6 @@ structure InvalidPokeballError { } @error("server") structure MasterBallUnsuccessful { - @required message: String, } @error("client") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index c7d7a00447..93295ab001 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -158,6 +158,11 @@ open class ServerCodegenVisitor( rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + + // TODO Traverse the model and error out if: + // * constraint traits on event streams are used. + // * constraint traits on streaming blob are used. + // * constraint trait precedence is used. } /** diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 901022be68..adbcd84117 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asType +import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate @@ -35,6 +36,7 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol @@ -225,18 +227,19 @@ class EventStreamUnmarshallerGenerator( } private fun RustWriter.renderUnmarshallEventHeader(member: MemberShape) { - val memberName = symbolProvider.toMemberName(member) - withBlock("builder = builder.$memberName(", ");") { - when (val target = model.expectShape(member.target)) { - is BooleanShape -> rustTemplate("#{expect_fns}::expect_bool(header)?", *codegenScope) - is ByteShape -> rustTemplate("#{expect_fns}::expect_byte(header)?", *codegenScope) - is ShortShape -> rustTemplate("#{expect_fns}::expect_int16(header)?", *codegenScope) - is IntegerShape -> rustTemplate("#{expect_fns}::expect_int32(header)?", *codegenScope) - is LongShape -> rustTemplate("#{expect_fns}::expect_int64(header)?", *codegenScope) - is BlobShape -> rustTemplate("#{expect_fns}::expect_byte_array(header)?", *codegenScope) - is StringShape -> rustTemplate("#{expect_fns}::expect_string(header)?", *codegenScope) - is TimestampShape -> rustTemplate("#{expect_fns}::expect_timestamp(header)?", *codegenScope) - else -> throw IllegalStateException("unsupported event stream header shape type: $target") + withBlock("builder = builder.${member.deserializerBuilderSetterName(codegenTarget)}(", ");") { + conditionalBlock("Some(", ")", member.isOptional) { + when (val target = model.expectShape(member.target)) { + is BooleanShape -> rustTemplate("#{expect_fns}::expect_bool(header)?", *codegenScope) + is ByteShape -> rustTemplate("#{expect_fns}::expect_byte(header)?", *codegenScope) + is ShortShape -> rustTemplate("#{expect_fns}::expect_int16(header)?", *codegenScope) + is IntegerShape -> rustTemplate("#{expect_fns}::expect_int32(header)?", *codegenScope) + is LongShape -> rustTemplate("#{expect_fns}::expect_int64(header)?", *codegenScope) + is BlobShape -> rustTemplate("#{expect_fns}::expect_byte_array(header)?", *codegenScope) + is StringShape -> rustTemplate("#{expect_fns}::expect_string(header)?", *codegenScope) + is TimestampShape -> rustTemplate("#{expect_fns}::expect_timestamp(header)?", *codegenScope) + else -> throw IllegalStateException("unsupported event stream header shape type: $target") + } } } } @@ -259,23 +262,24 @@ class EventStreamUnmarshallerGenerator( *codegenScope, ) } - val memberName = symbolProvider.toMemberName(member) - withBlock("builder = builder.$memberName(", ");") { - when (target) { - is BlobShape -> { - rustTemplate("#{Blob}::new(message.payload().as_ref())", *codegenScope) - } - is StringShape -> { - rustTemplate( - """ - std::str::from_utf8(message.payload()) - .map_err(|_| #{Error}::Unmarshalling("message payload is not valid UTF-8".into()))? - """, - *codegenScope, - ) - } - is UnionShape, is StructureShape -> { - renderParseProtocolPayload(member) + withBlock("builder = builder.${member.deserializerBuilderSetterName(codegenTarget)}(", ");") { + conditionalBlock("Some(", ")", member.isOptional) { + when (target) { + is BlobShape -> { + rustTemplate("#{Blob}::new(message.payload().as_ref())", *codegenScope) + } + is StringShape -> { + rustTemplate( + """ + std::str::from_utf8(message.payload()) + .map_err(|_| #{Error}::Unmarshalling("message payload is not valid UTF-8".into()))? + """, + *codegenScope, + ) + } + is UnionShape, is StructureShape -> { + renderParseProtocolPayload(member) + } } } } @@ -283,12 +287,11 @@ class EventStreamUnmarshallerGenerator( private fun RustWriter.renderParseProtocolPayload(member: MemberShape) { val parser = protocol.structuredDataParser(operationShape).payloadParser(member) - val memberName = symbolProvider.toMemberName(member) rustTemplate( """ #{parser}(&message.payload()[..]) .map_err(|err| { - #{Error}::Unmarshalling(format!("failed to unmarshall $memberName: {}", err)) + #{Error}::Unmarshalling(format!("failed to unmarshall ${member.memberName}: {}", err)) })? """, "parser" to parser, From b19ae9a4458ad1fe839a8cc10c9bb8a1b189945c Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 8 Sep 2022 11:08:40 +0200 Subject: [PATCH 163/255] fix server builder setter names, rest_json_extras server now works --- .../codegen/server/smithy/generators/ServerBuilderGenerator.kt | 2 +- codegen-test/model/rest-json-extras.smithy | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 32ee238987..0be84d868e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -389,7 +389,7 @@ class ServerBuilderGenerator( writer.documentShape(member, model) // Setter names will never hit a reserved word and therefore never need escaping. - writer.rustBlock("pub(crate) fn set_${memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { + writer.rustBlock("pub(crate) fn set_${member.memberName.toSnakeCase()}(mut self, input: $inputType) -> Self") { rust( """ self.$memberName = ${ diff --git a/codegen-test/model/rest-json-extras.smithy b/codegen-test/model/rest-json-extras.smithy index 748c1e3076..35c0bfcaeb 100644 --- a/codegen-test/model/rest-json-extras.smithy +++ b/codegen-test/model/rest-json-extras.smithy @@ -122,6 +122,7 @@ structure StringPayloadInput { documentation: "Primitive ints should not be serialized when they are unset", uri: "/primitive-document", method: "POST", + appliesTo: "client", body: "{}", headers: { "Content-Type": "application/json" }, params: {}, From 89c927b51aa4def3f2e4055572e5eb43b8296d77 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 8 Sep 2022 15:03:36 +0200 Subject: [PATCH 164/255] Handle enum shapes in UnconstrainedShapeSymbolProvider --- .../rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt index aa342ed495..d34f4f3d1a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/UnconstrainedShapeSymbolProvider.kt @@ -142,7 +142,7 @@ class UnconstrainedShapeSymbolProvider( } is StringShape -> { if (shape.canReachConstrainedShape(model, base)) { - symbolBuilder(shape, SimpleShapes.getValue(shape::class)).setDefault(Default.RustDefault).build() + symbolBuilder(shape, RustType.String).setDefault(Default.RustDefault).build() } else { base.toSymbol(shape) } From c40b158904c7088fbe1dabc4a7842790b51a4715 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 9 Sep 2022 18:13:16 +0200 Subject: [PATCH 165/255] avoid useless_conversion when converting from the pub(crate) constrained wrapper tuple types into what the user sees --- .../PubCrateConstrainedCollectionGenerator.kt | 14 ++++++++-- .../PubCrateConstrainedMapGenerator.kt | 20 +++++++------ .../smithy/rust/codegen/smithy/Constraints.kt | 28 +++++++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 76f146b8ea..706adb0b7e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbol import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.containsNonPublicType import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyConstrained @@ -92,10 +93,11 @@ class PubCrateConstrainedCollectionGenerator( // // Note that we could add the iteration code unconditionally and it would still be correct, but the `into()` calls // would be useless. Clippy flags this as [`useless_conversion`]. We could deactivate the lint, but it's probably - // best that we just don't emit a useless iteration, lest the compiler not optimize it away, and to make the - // generated code a little bit simpler. + // best that we just don't emit a useless iteration, lest the compiler not optimize it away (see [Godbolt]), + // and to make the generated code a little bit simpler. // // [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion. + // [Godbolt]: https://godbolt.org/z/eheWebWMa val innerNeedsConstraining = !innerShape.isDirectlyConstrained(symbolProvider) && (innerShape is CollectionShape || innerShape is MapShape) @@ -124,11 +126,17 @@ class PubCrateConstrainedCollectionGenerator( *codegenScope ) } else { + val innerNeedsConversion = innerShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + rustTemplate( """ impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { - v.0.into_iter().map(|item| item.into()).collect() + ${ if (innerNeedsConversion) { + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index ad75357dcd..b8e3e2f3c5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -16,17 +16,10 @@ import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbol import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.containsNonPublicType import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyConstrained -// TODO I'm looking at this class and `ConstrainedMapGenerator` and cannot help but think that we could dispense with -// the stuff `PubCrate*` (i.e. the entire `constrained module), by just generating the `Constrained*` shapes and -// marking them `pub(crate)`. After all, when `publicConstrainedTypes` is `false`, we're still generating the -// constrained types and using them in the deser path, but we're marking them `pub(crate)`, and it works fine. -// Maybe this approach is better, since this is less code than the public constrained types' generators, and it -// makes the distinction between what is directly constrained vs what is transitively but not directly constrained -// clearer. - /** * A generator for a wrapper tuple newtype over a map shape's symbol type. * @@ -124,11 +117,20 @@ class PubCrateConstrainedMapGenerator( *codegenScope ) } else { + val keyNeedsConversion = keyShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + val valueNeedsConversion = valueShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + rustTemplate( """ impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { - v.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect() + ${ if (keyNeedsConversion || valueNeedsConversion) { + val keyConversion = if (keyNeedsConversion) { ".into()" } else { "" } + val valueConversion = if (valueNeedsConversion) { ".into()" } else { "" } + "v.0.into_iter().map(|(k, v)| (k$keyConversion, v$valueConversion)).collect()" + } else { + "v.0" + } } } } """, diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 4583fc38f7..76daef195c 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -3,15 +3,19 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.SimpleShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.rust.codegen.util.UNREACHABLE import software.amazon.smithy.rust.codegen.util.hasTrait /** @@ -52,6 +56,30 @@ fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (th else -> false } +/** + * Returns whether a shape's type _name_ contains a non-public type when `publicConstrainedTypes` is `false`. + * + * For example, a `Vec` contains a non-public type, because `crate::model::LengthString` + * is `pub(crate)` when `publicConstrainedTypes` is `false` + * + * Note that a structure shape's type _definition_ may contain non-public types, but its _name_ is always public. + * + * Note how we short-circuit on `publicConstrainedTypes = true`, but we still require it to be passed in instead of laying + * the responsibility on the caller, for API safety usage. + */ +fun Shape.containsNonPublicType( + model: Model, + symbolProvider: SymbolProvider, + publicConstrainedTypes: Boolean, +): Boolean = !publicConstrainedTypes && when (this) { + is SimpleShape -> hasPublicConstrainedWrapperTupleType(model) + is MemberShape -> model.expectShape(this.target).containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + is CollectionShape -> this.canReachConstrainedShape(model, symbolProvider) + is MapShape -> this.canReachConstrainedShape(model, symbolProvider) + is StructureShape, is UnionShape -> false + else -> UNREACHABLE("the above arms should be exhaustive, but we received shape: $this") +} + fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) From e64203ec26ce6fc81fa7ced07ab2731f13985409 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 9 Sep 2022 18:44:03 +0200 Subject: [PATCH 166/255] clippy: fix redundant closure --- .../rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index 22cb9fee0f..c67ae9e421 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -321,7 +321,7 @@ class JsonParserGenerator( rustTemplate( """ #{expect_number_or_null}(tokens.next())? - .map(|v| #{NumberType}::try_from(v)) + .map(#{NumberType}::try_from) .transpose()? """, "NumberType" to symbolProvider.toSymbol(target), From 3111c7c529b934faeabe01f294f450d592c0093e Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 13:21:00 +0200 Subject: [PATCH 167/255] clippy: fix enum_variant_names --- .../codegen/server/smithy/generators/ServerBuilderGenerator.kt | 2 +- .../server/smithy/generators/UnconstrainedUnionGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 0be84d868e..a743343fa8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -578,7 +578,7 @@ enum class ConstraintViolationKind { data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { fun name() = when (kind) { ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}ConstraintViolation" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}" } /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 269754b3ee..c61ba7387b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -131,7 +131,7 @@ class UnconstrainedUnionGenerator( } data class ConstraintViolation(val forMember: MemberShape) { - fun name() = "${forMember.memberName.toPascalCase()}ConstraintViolation" + fun name() = forMember.memberName.toPascalCase() } private fun constraintViolations() = From 5487991b1cfc281b1aae7a7271cedbeb28607c3f Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 13:34:53 +0200 Subject: [PATCH 168/255] clippy: redundant unconstrained.into() when converting unions --- .../smithy/generators/UnconstrainedUnionGenerator.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index c61ba7387b..a2d72f450e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -6,7 +6,9 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -167,10 +169,9 @@ class UnconstrainedUnionGenerator( ) { if (member.targetCanReachConstrainedShape(model, symbolProvider)) { val targetShape = model.expectShape(member.target) - val resolveToNonPublicConstrainedType = !publicConstrainedTypes || - (!targetShape.isDirectlyConstrained(symbolProvider) && - !targetShape.isStructureShape && - !targetShape.isUnionShape) + val resolveToNonPublicConstrainedType = + targetShape !is StructureShape && targetShape !is UnionShape && !targetShape.hasTrait() + (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) val hasBox = member.hasTrait() if (hasBox) { From c3806eb15cfc13a5b9eba01f823a4027f82f8c82 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 14:08:43 +0200 Subject: [PATCH 169/255] constraints,constraints_without_public_constrained_types clippy work --- .../server/smithy/generators/UnconstrainedUnionGenerator.kt | 3 ++- .../software/amazon/smithy/rust/codegen/smithy/Constraints.kt | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index a2d72f450e..f749f6fd7d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -169,8 +169,9 @@ class UnconstrainedUnionGenerator( ) { if (member.targetCanReachConstrainedShape(model, symbolProvider)) { val targetShape = model.expectShape(member.target) + // TODO Use `hasPublicConstrainedWrapperTupleType`. val resolveToNonPublicConstrainedType = - targetShape !is StructureShape && targetShape !is UnionShape && !targetShape.hasTrait() + targetShape !is StructureShape && targetShape !is UnionShape && !targetShape.hasTrait() && (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) val hasBox = member.hasTrait() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 76daef195c..486b649bc1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -64,8 +64,8 @@ fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (th * * Note that a structure shape's type _definition_ may contain non-public types, but its _name_ is always public. * - * Note how we short-circuit on `publicConstrainedTypes = true`, but we still require it to be passed in instead of laying - * the responsibility on the caller, for API safety usage. + * Note how we short-circuit on `publicConstrainedTypes = true`, but we still require it to be passed in instead of laying + * the responsibility on the caller, for API safety usage. */ fun Shape.containsNonPublicType( model: Model, From dbf7d9e5d7388e4b296ca406cd077ec18cb12458 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 14:22:10 +0200 Subject: [PATCH 170/255] Refactor to use `workingWithPublicConstrainedWrapperTupleType` in `Constraints.kt`, make `hasPublicConstrainedWrapperTupleType` API safer, `constraints,constraints_without_public_constrained_types` up to clippy work --- .../generators/ServerBuilderGenerator.kt | 4 +-- .../server/smithy/protocols/ServerAwsJson.kt | 2 -- .../server/smithy/protocols/ServerRestJson.kt | 1 - .../smithy/rust/codegen/smithy/Constraints.kt | 25 ++++++++++++++----- .../generators/http/HttpBindingGenerator.kt | 12 +++------ .../serialize/JsonSerializerGenerator.kt | 14 +++-------- .../XmlBindingTraitSerializerGenerator.kt | 9 +++---- 7 files changed, 31 insertions(+), 36 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index a743343fa8..5bd23fc979 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -43,7 +43,6 @@ import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.smithy.letIf @@ -54,6 +53,7 @@ import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -548,7 +548,7 @@ class ServerBuilderGenerator( // We've just checked the constraints hold by going through the non-public // constrained type, but the user wants to work with the unconstrained type, so we have to // unwrap it. - if (!publicConstrainedTypes && member.hasPublicConstrainedWrapperTupleType(model)) { + if (!publicConstrainedTypes && member.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model)) { rust( ".map(|v: #T| v.into())", constrainedShapeSymbolProvider.toSymbol(model.expectShape(member.target)), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index f3bed2325d..e270555a5b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.escape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable -import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.smithy.protocols.AwsJson @@ -91,7 +90,6 @@ class ServerAwsJsonSerializerGenerator( httpBindingResolver, ::awsJsonFieldName, customizations = listOf(ServerAwsJsonError(awsJsonVersion)), - publicConstrainedTypes = serverCodegenContext.settings.codegenConfig.publicConstrainedTypes ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index d724f4e727..c45a7e87a5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -47,6 +47,5 @@ class ServerRestJson(private val serverCodegenContext: ServerCodegenContext) : R serverCodegenContext, httpBindingResolver, ::restJsonFieldName, - publicConstrainedTypes = serverCodegenContext.settings.codegenConfig.publicConstrainedTypes, ) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index 486b649bc1..c0044cba3f 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.util.UNREACHABLE import software.amazon.smithy.rust.codegen.util.hasTrait @@ -48,14 +49,26 @@ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { else -> false } -// TODO Make this method take in the `publicConstrainedTypes` boolean, even if it is just going to AND it with the rest of the condition, for API safety usage. -fun Shape.hasPublicConstrainedWrapperTupleType(model: Model): Boolean = when (this) { - is MapShape -> this.hasTrait() - is StringShape -> !this.hasTrait() && this.hasTrait() - is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model) +fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTypes: Boolean): Boolean = when (this) { + is MapShape -> publicConstrainedTypes && this.hasTrait() + is StringShape -> !this.hasTrait() && (publicConstrainedTypes && this.hasTrait()) + is MemberShape -> model.expectShape(this.target).hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes) else -> false } +fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model: Model) = + hasPublicConstrainedWrapperTupleType(model, true) + +/** + * Helper function to determine whether a shape will map to a _public_ constrained wrapper tuple type. + */ +fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, coreCodegenContext: CoreCodegenContext) = + coreCodegenContext.target == CodegenTarget.SERVER && + shape.hasPublicConstrainedWrapperTupleType( + coreCodegenContext.model, + (coreCodegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes, + ) + /** * Returns whether a shape's type _name_ contains a non-public type when `publicConstrainedTypes` is `false`. * @@ -72,7 +85,7 @@ fun Shape.containsNonPublicType( symbolProvider: SymbolProvider, publicConstrainedTypes: Boolean, ): Boolean = !publicConstrainedTypes && when (this) { - is SimpleShape -> hasPublicConstrainedWrapperTupleType(model) + is SimpleShape -> wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model) is MemberShape -> model.expectShape(this.target).containsNonPublicType(model, symbolProvider, publicConstrainedTypes) is CollectionShape -> this.canReachConstrainedShape(model, symbolProvider) is MapShape -> this.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index a6f9881d8b..18e066d5d3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -38,12 +38,10 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.generators.redactIfNecessary -import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.makeOptional import software.amazon.smithy.rust.codegen.smithy.mapRustType import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingDescriptor @@ -52,6 +50,7 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.smithy.protocols.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.util.UNREACHABLE import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.hasTrait @@ -556,7 +555,7 @@ class HttpBindingGenerator( val listHeader = memberType is CollectionShape rustTemplate( """ - for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType(memberShape)) "&$field.0" else field } { + for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType(memberShape, coreCodegenContext)) "&$field.0" else field } { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} @@ -603,7 +602,7 @@ class HttpBindingGenerator( } else { // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member traits are not // supported yet. - val expr = if (workingWithPublicConstrainedWrapperTupleType(target)) "&$targetName.0" else targetName + val expr = if (workingWithPublicConstrainedWrapperTupleType(target, coreCodegenContext)) "&$targetName.0" else targetName quoteValue("AsRef::::as_ref($expr)") } } @@ -622,9 +621,4 @@ class HttpBindingGenerator( else -> throw CodegenException("unexpected shape: $target") } } - - private fun workingWithPublicConstrainedWrapperTupleType(shape: Shape) = - codegenTarget == CodegenTarget.SERVER && - (coreCodegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes && - shape.hasPublicConstrainedWrapperTupleType(model) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt index ba3deba05b..5d5ffa4054 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -36,18 +36,17 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.customize.NamedSectionGenerator import software.amazon.smithy.rust.codegen.smithy.customize.Section -import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.generators.serializationError -import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.smithy.protocols.serializeFunctionName import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.hasTrait @@ -68,12 +67,11 @@ sealed class JsonSection(name: String) : Section(name) { typealias JsonCustomization = NamedSectionGenerator class JsonSerializerGenerator( - coreCodegenContext: CoreCodegenContext, + private val coreCodegenContext: CoreCodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, private val customizations: List = listOf(), - private val publicConstrainedTypes: Boolean = false, ) : StructuredDataSerializerGenerator { private data class Context( /** Expression that retrieves a JsonValueWriter from either a JsonObjectWriter or JsonArrayWriter */ @@ -353,9 +351,7 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMemberValue(context: MemberContext, target: Shape) { val writer = context.writerExpression - val workingWithPublicConstrainedWrapperTupleType = - codegenTarget == CodegenTarget.SERVER && context.shape.hasPublicConstrainedWrapperTupleType(model) - val value = if (publicConstrainedTypes && workingWithPublicConstrainedWrapperTupleType) { + val value = if (workingWithPublicConstrainedWrapperTupleType(context.shape, coreCodegenContext)) { ValueExpression.Value("${context.valueExpression.name}.0") } else { context.valueExpression @@ -435,9 +431,7 @@ class JsonSerializerGenerator( val valueName = safeName("value") rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { val keyTarget = model.expectShape(context.shape.key.target) - val workingWithPublicConstrainedWrapperTupleType = - codegenTarget == CodegenTarget.SERVER && keyTarget.hasPublicConstrainedWrapperTupleType(model) - val keyExpression = if (publicConstrainedTypes && workingWithPublicConstrainedWrapperTupleType) { + val keyExpression = if (workingWithPublicConstrainedWrapperTupleType(keyTarget, coreCodegenContext)) { "$keyName.0.as_str()" } else if (keyTarget.hasTrait()) { "$keyName.as_str()" diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index a32f982dbc..eb8685a6aa 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -38,11 +38,9 @@ import software.amazon.smithy.rust.codegen.rustlang.stripOuter import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.smithy.generators.serializationError -import software.amazon.smithy.rust.codegen.smithy.hasPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.smithy.isOptional import software.amazon.smithy.rust.codegen.smithy.letIf import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBindingResolver @@ -51,6 +49,7 @@ import software.amazon.smithy.rust.codegen.smithy.protocols.XmlMemberIndex import software.amazon.smithy.rust.codegen.smithy.protocols.XmlNameIndex import software.amazon.smithy.rust.codegen.smithy.protocols.serializeFunctionName import software.amazon.smithy.rust.codegen.smithy.rustType +import software.amazon.smithy.rust.codegen.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.util.dq import software.amazon.smithy.rust.codegen.util.getTrait import software.amazon.smithy.rust.codegen.util.hasTrait @@ -58,7 +57,7 @@ import software.amazon.smithy.rust.codegen.util.inputShape import software.amazon.smithy.rust.codegen.util.outputShape class XmlBindingTraitSerializerGenerator( - coreCodegenContext: CoreCodegenContext, + private val coreCodegenContext: CoreCodegenContext, private val httpBindingResolver: HttpBindingResolver, ) : StructuredDataSerializerGenerator { private val symbolProvider = coreCodegenContext.symbolProvider @@ -289,9 +288,7 @@ class XmlBindingTraitSerializerGenerator( private fun RustWriter.serializeRawMember(member: MemberShape, input: String) { when (model.expectShape(member.target)) { is StringShape -> { - val workingWithPublicConstrainedWrapperTupleType = - codegenTarget == CodegenTarget.SERVER && member.hasPublicConstrainedWrapperTupleType(model) - if (workingWithPublicConstrainedWrapperTupleType) { + if (workingWithPublicConstrainedWrapperTupleType(member, coreCodegenContext)) { rust("$input.0.as_str()") } else { rust("$input.as_str()") From 0789fce0543ac5cf27f9840d1861e995bce5c7f2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 15:15:22 +0200 Subject: [PATCH 171/255] Don't use curly braces where not needed; ---- ssa works ---- --- .../generators/UnconstrainedUnionGenerator.kt | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index f749f6fd7d..7264d439e8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -163,20 +163,22 @@ class UnconstrainedUnionGenerator( sortedMembers.forEach { member -> val memberName = unconstrainedShapeSymbolProvider.toMemberName(member) withBlockTemplate( - "#{UnconstrainedUnion}::$memberName(unconstrained) => Self::$memberName({", - "}),", + "#{UnconstrainedUnion}::$memberName(unconstrained) => Self::$memberName(", + "),", "UnconstrainedUnion" to symbol, ) { - if (member.targetCanReachConstrainedShape(model, symbolProvider)) { + if (!member.canReachConstrainedShape(model, symbolProvider)) { + rust("unconstrained") + } else { val targetShape = model.expectShape(member.target) - // TODO Use `hasPublicConstrainedWrapperTupleType`. val resolveToNonPublicConstrainedType = targetShape !is StructureShape && targetShape !is UnionShape && !targetShape.hasTrait() && (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) - val hasBox = member.hasTrait() - if (hasBox) { - rust("let unconstrained = *unconstrained;") + val (unconstrainedVar, boxIt) = if (member.hasTrait()) { + "(*unconstrained)" to ".map(Box::new).map_err(Box::new)" + } else { + "unconstrained" to "" } if (resolveToNonPublicConstrainedType) { @@ -188,26 +190,26 @@ class UnconstrainedUnionGenerator( } rustTemplate( """ - let constrained: #{ConstrainedSymbol} = unconstrained - .try_into() - ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } - .map_err(Self::Error::${ConstraintViolation(member).name()})?; - constrained.into() + { + let constrained: #{ConstrainedSymbol} = $unconstrainedVar + .try_into() + $boxIt + .map_err(Self::Error::${ConstraintViolation(member).name()})?; + constrained.into() + } """, "ConstrainedSymbol" to constrainedSymbol ) } else { rust( """ - unconstrained + $unconstrainedVar .try_into() - ${ if (hasBox) ".map(Box::new).map_err(Box::new)" else "" } + $boxIt .map_err(Self::Error::${ConstraintViolation(member).name()})? """ ) } - } else { - rust("unconstrained") } } } From d6e3f89d30ef09b364471e99f28684bcdf21f252 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 16:20:47 +0200 Subject: [PATCH 172/255] sa, ca work --- codegen-test/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-test/build.gradle.kts b/codegen-test/build.gradle.kts index 484ad0ef1d..a74c2bb5f2 100644 --- a/codegen-test/build.gradle.kts +++ b/codegen-test/build.gradle.kts @@ -36,7 +36,7 @@ dependencies { val allCodegenTests = listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple"), - CodegenTest("com.amazonaws.simple#ConstraintsService", "constraints"), + CodegenTest("com.amazonaws.constraints#ConstraintsService", "constraints"), CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), CodegenTest("com.amazonaws.ebs#Ebs", "ebs"), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), From 95699280e111702c303b519dcc8672bd6a10dee7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 17:50:57 +0200 Subject: [PATCH 173/255] =?UTF-8?q?Update=20Pok=C3=A9mon=20Service=20to=20?= =?UTF-8?q?work=20with=20constraint=20traits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../examples/pokemon-service/src/lib.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs index 0089f1557c..422f49c828 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs @@ -211,7 +211,7 @@ pub async fn capture_pokemon( ) -> Result { if input.region != "Kanto" { return Err(error::CapturePokemonOperationError::UnsupportedRegionError( - error::UnsupportedRegionError::builder().build(), + error::UnsupportedRegionError { region: input.region }, )); } let output_stream = stream! { @@ -227,7 +227,9 @@ pub async fn capture_pokemon( if ! matches!(pokeball, "Master Ball" | "Great Ball" | "Fast Ball") { yield Err( crate::error::CapturePokemonEventsError::InvalidPokeballError( - crate::error::InvalidPokeballError::builder().pokeball(pokeball).build() + crate::error::InvalidPokeballError { + pokeball: pokeball.to_owned() + } ) ); } else { @@ -250,11 +252,12 @@ pub async fn capture_pokemon( .to_string(); let pokedex: Vec = (0..255).collect(); yield Ok(crate::model::CapturePokemonEvents::Event( - crate::model::CaptureEvent::builder() - .name(pokemon) - .shiny(shiny) - .pokedex_update(Blob::new(pokedex)) - .build(), + crate::model::CaptureEvent { + name: Some(pokemon), + shiny: Some(shiny), + pokedex_update: Some(Blob::new(pokedex)), + captured: Some(true), + } )); } } From d9abb86f6133a80b4244a19d6ad1dbd533c23e36 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 12 Sep 2022 18:22:29 +0200 Subject: [PATCH 174/255] ./gradlew ktlintFormat --- .../smithy/PythonServerCodegenVisitor.kt | 2 +- .../generators/PythonServerEnumGenerator.kt | 2 +- .../server/smithy/RustCodegenServerPlugin.kt | 4 +- .../server/smithy/ServerCodegenVisitor.kt | 22 +++++----- .../generators/ConstrainedMapGenerator.kt | 8 ++-- .../ConstrainedShapeGeneratorCommon.kt | 4 +- .../generators/ConstrainedStringGenerator.kt | 8 ++-- .../ConstrainedTraitForEnumGenerator.kt | 2 +- .../MapConstraintViolationGenerator.kt | 6 +-- .../PubCrateConstrainedCollectionGenerator.kt | 32 +++++++------- .../PubCrateConstrainedMapGenerator.kt | 36 ++++++++-------- .../generators/ServerBuilderGenerator.kt | 42 +++++++++---------- ...rGeneratorWithoutPublicConstrainedTypes.kt | 9 ++-- .../smithy/generators/ServerEnumGenerator.kt | 8 ++-- .../ServerStructureConstrainedTraitImpl.kt | 4 +- .../UnconstrainedCollectionGenerator.kt | 6 +-- .../generators/UnconstrainedMapGenerator.kt | 12 +++--- .../generators/UnconstrainedUnionGenerator.kt | 20 ++++----- .../http/ServerRequestBindingGenerator.kt | 2 +- .../ServerHttpBoundProtocolGenerator.kt | 18 ++++---- .../smithy/testutil/ServerTestHelpers.kt | 8 ++-- ...CrateConstrainedShapeSymbolProviderTest.kt | 8 ++-- .../generators/ConstrainedMapGeneratorTest.kt | 26 ++++++------ .../ConstrainedStringGeneratorTest.kt | 22 +++++----- .../generators/ServerEnumGeneratorTest.kt | 10 ++--- .../UnconstrainedCollectionGeneratorTest.kt | 12 +++--- .../UnconstrainedMapGeneratorTest.kt | 14 +++---- .../UnconstrainedUnionGeneratorTest.kt | 10 ++--- .../rust/codegen/smithy/CodegenDelegator.kt | 2 +- .../ConstraintViolationSymbolProvider.kt | 12 +++--- .../smithy/rust/codegen/smithy/Constraints.kt | 8 ++-- .../rust/codegen/smithy/ServerRustSettings.kt | 2 +- .../rust/codegen/smithy/SymbolVisitor.kt | 6 +-- .../smithy/customize/RustCodegenDecorator.kt | 1 - .../codegen/smithy/generators/Instantiator.kt | 2 +- .../smithy/generators/error/ErrorGenerator.kt | 4 +- .../generators/http/HttpBindingGenerator.kt | 16 +++---- .../protocols/parse/JsonParserGenerator.kt | 19 +++++---- .../HttpVersionListGeneratorTest.kt | 39 +++++++++-------- 39 files changed, 238 insertions(+), 230 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index c999cb5e5f..6aa94e239d 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -112,7 +112,7 @@ class PythonServerCodegenVisitor( constraintViolationSymbolProvider, writer, shape, - enum + enum, ).render() } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index ab6072bb26..0668ae3d2e 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -17,8 +17,8 @@ import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.util.dq diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 6102716858..91eaf24f9d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -63,7 +63,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - publicConstrainedTypes: Boolean = true + publicConstrainedTypes: Boolean = true, ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs @@ -71,7 +71,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { if (publicConstrainedTypes) ConstrainedShapeSymbolProvider( it, model, - serviceShape + serviceShape, ) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 93295ab001..1cfac48a02 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -244,7 +244,7 @@ open class ServerCodegenVisitor( val serverBuilderGenerator = ServerBuilderGenerator( codegenContext, shape, - if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null + if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null, ) serverBuilderGenerator.render(writer) @@ -282,7 +282,7 @@ open class ServerCodegenVisitor( private fun collectionShape(shape: CollectionShape) { if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider + symbolProvider, ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for collection shape $shape") @@ -293,7 +293,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, - shape + shape, ).render() } } @@ -304,7 +304,7 @@ open class ServerCodegenVisitor( codegenContext, pubCrateConstrainedShapeSymbolProvider, writer, - shape + shape, ).render() } } @@ -314,7 +314,7 @@ open class ServerCodegenVisitor( val renderUnconstrainedMap = shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider + symbolProvider, ) if (renderUnconstrainedMap) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") @@ -324,7 +324,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, unconstrainedShapeSymbolProvider, unconstrainedModuleWriter, - shape + shape, ).render() } @@ -335,7 +335,7 @@ open class ServerCodegenVisitor( codegenContext, pubCrateConstrainedShapeSymbolProvider, writer, - shape + shape, ).render() } } @@ -348,7 +348,7 @@ open class ServerCodegenVisitor( codegenContext, modelsModuleWriter, shape, - if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null + if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null, ).render() } } @@ -381,7 +381,7 @@ open class ServerCodegenVisitor( spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the constraints (which it currently does not), not each code generator. See https://github.com/awslabs/smithy/issues/1121f for more information. - """.trimIndent() + """.trimIndent(), ) } else if (shape.isDirectlyConstrained(symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") @@ -406,7 +406,7 @@ open class ServerCodegenVisitor( if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider + symbolProvider, ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for union shape $shape") @@ -417,7 +417,7 @@ open class ServerCodegenVisitor( pubCrateConstrainedShapeSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, - shape + shape, ).render() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 67eb3b6a3d..101521dfdf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -41,7 +41,7 @@ class ConstrainedMapGenerator( private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -73,7 +73,7 @@ class ConstrainedMapGenerator( } val constrainedTypeMetadata = RustMetadata( Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.Clone, RuntimeType.PartialEq)), - visibility = constrainedTypeVisibility + visibility = constrainedTypeVisibility, ) val codegenScope = arrayOf( @@ -81,7 +81,7 @@ class ConstrainedMapGenerator( "ValueSymbol" to constrainedShapeSymbolProvider.toSymbol(model.expectShape(shape.value.target)), "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, - "ConstraintViolation" to constraintViolation + "ConstraintViolation" to constraintViolation, ) // TODO Display impl missing; it should honor `sensitive` trait. @@ -130,7 +130,7 @@ class ConstrainedMapGenerator( } } """, - *codegenScope + *codegenScope, ) if (!publicConstrainedTypes && isValueConstrained(shape, model, symbolProvider)) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt index bf67e043df..d372a33582 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt @@ -11,8 +11,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators fun rustDocsNote(typeName: String) = "this is a constrained type because its corresponding modeled Smithy shape has one or more " + - "[constraint traits]. Use [`parse`] or [`$typeName::TryFrom`] to construct values of this type." + - "[constraint traits]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html" + "[constraint traits]. Use [`parse`] or [`$typeName::TryFrom`] to construct values of this type." + + "[constraint traits]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html" fun rustDocsParseMethod(typeName: String, inner: String) = "Constructs a `$typeName` from an [`$inner`], failing when the provided value does not satisfy the modeled constraints." diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index e76bf5e8b6..586aa60298 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -30,13 +30,13 @@ import software.amazon.smithy.rust.codegen.util.expectTrait class ConstrainedStringGenerator( val codegenContext: ServerCodegenContext, val writer: RustWriter, - val shape: StringShape + val shape: StringShape, ) { val model = codegenContext.model val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -67,7 +67,7 @@ class ConstrainedStringGenerator( } val constrainedTypeMetadata = RustMetadata( Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.Clone, RuntimeType.PartialEq, RuntimeType.Eq, RuntimeType.Hash)), - visibility = constrainedTypeVisibility + visibility = constrainedTypeVisibility, ) // TODO Display impl does not honor `sensitive` trait. @@ -150,7 +150,7 @@ class ConstrainedStringGenerator( pub enum ${constraintViolation.name} { Length(usize), } - """ + """, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index ebbaea1427..7405f08003 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -22,7 +22,7 @@ class ConstrainedTraitForEnumGenerator( val model: Model, val symbolProvider: RustSymbolProvider, val writer: RustWriter, - val shape: StringShape + val shape: StringShape, ) { fun render() { shape.expectTrait() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index b68398e735..2a2c1ad72b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -19,13 +19,13 @@ import software.amazon.smithy.rust.codegen.util.hasTrait class MapConstraintViolationGenerator( codegenContext: ServerCodegenContext, private val modelsModuleWriter: RustWriter, - val shape: MapShape + val shape: MapShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -59,7 +59,7 @@ class MapConstraintViolationGenerator( } modelsModuleWriter.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), - RustMetadata(visibility = constraintViolationVisibility) + RustMetadata(visibility = constraintViolationVisibility), ) { // TODO We should really have two `ConstraintViolation` types here. One will just have variants for each // constraint trait on the map shape, for use by the user. The other one will have variants if the shape's diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 706adb0b7e..ca038a87d8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -39,7 +39,7 @@ class PubCrateConstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, - val shape: CollectionShape + val shape: CollectionShape, ) { private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes @@ -81,7 +81,7 @@ class PubCrateConstrainedCollectionGenerator( type Unconstrained = #{UnconstrainedSymbol}; } """, - *codegenScope + *codegenScope, ) if (publicConstrainedTypes) { @@ -106,24 +106,24 @@ class PubCrateConstrainedCollectionGenerator( impl #{From}<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { ${ if (innerNeedsConstraining) { - "Self(v.into_iter().map(|item| item.into()).collect())" - } else { - "Self(v)" - } } + "Self(v.into_iter().map(|item| item.into()).collect())" + } else { + "Self(v)" + } } } } impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ if (innerNeedsConstraining) { - "v.0.into_iter().map(|item| item.into()).collect()" - } else { - "v.0" - } } + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } } } """, - *codegenScope + *codegenScope, ) } else { val innerNeedsConversion = innerShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) @@ -133,14 +133,14 @@ class PubCrateConstrainedCollectionGenerator( impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ if (innerNeedsConversion) { - "v.0.into_iter().map(|item| item.into()).collect()" - } else { - "v.0" - } } + "v.0.into_iter().map(|item| item.into()).collect()" + } else { + "v.0" + } } } } """, - *codegenScope + *codegenScope, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index b8e3e2f3c5..16d5093f96 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -38,7 +38,7 @@ class PubCrateConstrainedMapGenerator( val codegenContext: ServerCodegenContext, private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, - val shape: MapShape + val shape: MapShape, ) { private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes @@ -82,7 +82,7 @@ class PubCrateConstrainedMapGenerator( type Unconstrained = #{UnconstrainedSymbol}; } """, - *codegenScope + *codegenScope, ) if (publicConstrainedTypes) { @@ -97,24 +97,24 @@ class PubCrateConstrainedMapGenerator( impl #{From}<#{Symbol}> for $name { fn from(v: #{Symbol}) -> Self { ${ if (innerNeedsConstraining) { - "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" - } else { - "Self(v)" - } } + "Self(v.into_iter().map(|(k, v)| (k, v.into())).collect())" + } else { + "Self(v)" + } } } } impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ if (innerNeedsConstraining) { - "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" - } else { - "v.0" - } } + "v.0.into_iter().map(|(k, v)| (k, v.into())).collect()" + } else { + "v.0" + } } } } """, - *codegenScope + *codegenScope, ) } else { val keyNeedsConversion = keyShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) @@ -125,16 +125,16 @@ class PubCrateConstrainedMapGenerator( impl #{From}<$name> for #{Symbol} { fn from(v: $name) -> Self { ${ if (keyNeedsConversion || valueNeedsConversion) { - val keyConversion = if (keyNeedsConversion) { ".into()" } else { "" } - val valueConversion = if (valueNeedsConversion) { ".into()" } else { "" } - "v.0.into_iter().map(|(k, v)| (k$keyConversion, v$valueConversion)).collect()" - } else { - "v.0" - } } + val keyConversion = if (keyNeedsConversion) { ".into()" } else { "" } + val valueConversion = if (valueNeedsConversion) { ".into()" } else { "" } + "v.0.into_iter().map(|(k, v)| (k$keyConversion, v$valueConversion)).collect()" + } else { + "v.0" + } } } } """, - *codegenScope + *codegenScope, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 5bd23fc979..979fa2b4c8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -75,7 +75,7 @@ class ServerBuilderGenerator( private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val symbolProvider = codegenContext.symbolProvider private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -94,7 +94,7 @@ class ServerBuilderGenerator( "Structure" to structureSymbol, "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, - "MaybeConstrained" to RuntimeType.MaybeConstrained() + "MaybeConstrained" to RuntimeType.MaybeConstrained(), ) fun render(writer: RustWriter) { @@ -198,7 +198,7 @@ class ServerBuilderGenerator( } } """, - *codegenScope + *codegenScope, ) } @@ -212,7 +212,7 @@ class ServerBuilderGenerator( } """, *codegenScope, - "StructureMaybeConstrained" to structureSymbol.makeMaybeConstrained() + "StructureMaybeConstrained" to structureSymbol.makeMaybeConstrained(), ) } @@ -232,7 +232,7 @@ class ServerBuilderGenerator( """ The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. """, - structureSymbol + structureSymbol, ) if (constraintViolations().size > 1) { @@ -319,10 +319,10 @@ class ServerBuilderGenerator( !(targetShape is StringShape && targetShape.hasTrait()) && targetShape !is StructureShape - val maybeConstrainedVariant = if (isInputFullyUnconstrained) + val maybeConstrainedVariant = if (isInputFullyUnconstrained) { // Step 2 above. "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" - else { + } else { "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" } @@ -361,8 +361,8 @@ class ServerBuilderGenerator( private fun constrainedTypeHoldsFinalType(member: MemberShape): Boolean { val targetShape = model.expectShape(member.target) return targetShape is StructureShape || - targetShape is UnionShape || - member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider) + targetShape is UnionShape || + member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider) } /** @@ -383,7 +383,7 @@ class ServerBuilderGenerator( // The only reason why this condition can't simply be `member.isOptional` // is because non-`required` blob streaming members are interpreted as // `required`, so we can't use `member.isOptional` here. - symbolProvider.toSymbol(member).isOptional() + symbolProvider.toSymbol(member).isOptional(), ) { "Option<$it>" } val memberName = symbolProvider.toMemberName(member) @@ -401,7 +401,7 @@ class ServerBuilderGenerator( } }; self - """ + """, ) } } @@ -434,7 +434,7 @@ class ServerBuilderGenerator( } } """, - *codegenScope + *codegenScope, ) } @@ -447,7 +447,7 @@ class ServerBuilderGenerator( } } """, - *codegenScope + *codegenScope, ) } @@ -462,8 +462,8 @@ class ServerBuilderGenerator( } else { pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) } - // Strip the `Option` in case the member is not `required`. - .mapRustType { it.stripOuter() } + // Strip the `Option` in case the member is not `required`. + .mapRustType { it.stripOuter() } val hadBox = strippedOption.isRustBoxed() strippedOption @@ -525,7 +525,7 @@ class ServerBuilderGenerator( ) .transpose()? """, - *codegenScope + *codegenScope, ) } else { rustTemplate( @@ -571,6 +571,7 @@ class ServerBuilderGenerator( enum class ConstraintViolationKind { // A field is required but was not provided. MISSING_MEMBER, + // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. CONSTRAINED_SHAPE_FAILURE, } @@ -628,10 +629,10 @@ fun renderConstraintViolation( ConstraintViolationKind.MISSING_MEMBER -> { writer.docs( "${constraintViolationMessage( - constraintViolation, - symbolProvider, - structureSymbol, - ).replaceFirstChar { it.uppercase() }}.", + constraintViolation, + symbolProvider, + structureSymbol, + ).replaceFirstChar { it.uppercase() }}.", ) writer.rust("${constraintViolation.name()},") } @@ -652,4 +653,3 @@ fun renderConstraintViolation( writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } } - diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 005b9e4347..56873ba907 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -47,6 +47,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) + // TODO moduleName private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, false) private val moduleName = builderSymbol.namespace.split("::").last() @@ -57,7 +58,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( "Structure" to structureSymbol, "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, - "MaybeConstrained" to RuntimeType.MaybeConstrained() + "MaybeConstrained" to RuntimeType.MaybeConstrained(), ) fun render(writer: RustWriter) { @@ -146,7 +147,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( """ The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. """, - structureSymbol + structureSymbol, ) } // TODO Could be single block @@ -230,7 +231,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } } """, - *codegenScope + *codegenScope, ) } @@ -243,7 +244,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } } """, - *codegenScope + *codegenScope, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index b7a359b839..b597f29e4a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -27,7 +27,7 @@ open class ServerEnumGenerator( private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -39,7 +39,7 @@ open class ServerEnumGenerator( override fun renderFromForStr() { writer.withModule( - constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last() + constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), ) { rustTemplate( """ @@ -71,7 +71,7 @@ open class ServerEnumGenerator( """, "String" to RuntimeType.String, "TryFrom" to RuntimeType.TryFrom, - "UnknownVariantSymbol" to constraintViolationSymbol + "UnknownVariantSymbol" to constraintViolationSymbol, ) } @@ -85,7 +85,7 @@ open class ServerEnumGenerator( } } """, - "UnknownVariantSymbol" to constraintViolationSymbol + "UnknownVariantSymbol" to constraintViolationSymbol, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt index 269f3ca97a..c1f9e74f76 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -16,7 +16,7 @@ class ServerStructureConstrainedTraitImpl( private val symbolProvider: RustSymbolProvider, private val publicConstrainedTypes: Boolean, private val shape: StructureShape, - private val writer: RustWriter + private val writer: RustWriter, ) { fun render() { writer.rustTemplate( @@ -27,7 +27,7 @@ class ServerStructureConstrainedTraitImpl( """, "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "Structure" to symbolProvider.toSymbol(shape), - "Builder" to shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) + "Builder" to shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index d59cfa33cb..93736e7cb1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -23,14 +23,14 @@ class UnconstrainedCollectionGenerator( private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, - val shape: CollectionShape + val shape: CollectionShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -93,7 +93,7 @@ class UnconstrainedCollectionGenerator( } modelsModuleWriter.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), - RustMetadata(visibility = constraintViolationVisibility) + RustMetadata(visibility = constraintViolationVisibility), ) { rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 6bae1333e5..9aec20b988 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -27,7 +27,7 @@ class UnconstrainedMapGenerator( private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, - val shape: MapShape + val shape: MapShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -35,7 +35,7 @@ class UnconstrainedMapGenerator( private val name = symbol.name private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -90,8 +90,8 @@ class UnconstrainedMapGenerator( // TODO I think this breaks if the value shape is a constrained enum (?) Add protocol test. val resolveToNonPublicConstrainedValueType = isValueConstrained(valueShape, model, symbolProvider) && - !valueShape.isDirectlyConstrained(symbolProvider) && - !valueShape.isStructureShape + !valueShape.isDirectlyConstrained(symbolProvider) && + !valueShape.isStructureShape val constrainedValueSymbol = if (resolveToNonPublicConstrainedValueType) { pubCrateConstrainedShapeSymbolProvider.toSymbol(valueShape) } else { @@ -111,7 +111,7 @@ class UnconstrainedMapGenerator( let hm = res?; """, "ConstrainedKeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape), - "ConstrainedValueSymbol" to constrainedValueSymbol + "ConstrainedValueSymbol" to constrainedValueSymbol, ) val constrainedValueTypeIsNotFinalType = @@ -147,7 +147,7 @@ class UnconstrainedMapGenerator( hm.into_iter().map(|(k, v)| (k, v.into())).collect(); """, "KeySymbol" to symbolProvider.toSymbol(keyShape), - "ValueSymbol" to symbolProvider.toSymbol(valueShape) + "ValueSymbol" to symbolProvider.toSymbol(valueShape), ) } } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 7264d439e8..20ef404c4d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -39,14 +39,14 @@ class UnconstrainedUnionGenerator( private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, - val shape: UnionShape + val shape: UnionShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = - with (codegenContext.constraintViolationSymbolProvider) { + with(codegenContext.constraintViolationSymbolProvider) { if (publicConstrainedTypes) { this } else { @@ -71,12 +71,12 @@ class UnconstrainedUnionGenerator( ##[allow(clippy::enum_variant_names)] ##[derive(Debug, Clone)] pub(crate) enum $name - """ + """, ) { sortedMembers.forEach { member -> rust( "${unconstrainedShapeSymbolProvider.toMemberName(member)}(#T),", - unconstrainedShapeSymbolProvider.toSymbol(member) + unconstrainedShapeSymbolProvider.toSymbol(member), ) } } @@ -113,7 +113,7 @@ class UnconstrainedUnionGenerator( "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "ConstrainedSymbol" to constrainedSymbol, - "UnconstrainedSymbol" to symbol + "UnconstrainedSymbol" to symbol, ) val constraintViolationVisibility = if (publicConstrainedTypes) { @@ -123,7 +123,7 @@ class UnconstrainedUnionGenerator( } modelsModuleWriter.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), - RustMetadata(visibility = constraintViolationVisibility) + RustMetadata(visibility = constraintViolationVisibility), ) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(this) rustBlock("pub enum $constraintViolationName") { @@ -153,7 +153,7 @@ class UnconstrainedUnionGenerator( writer.rust( "${constraintViolation.name()}(#T),", - constraintViolationSymbol + constraintViolationSymbol, ) } @@ -173,7 +173,7 @@ class UnconstrainedUnionGenerator( val targetShape = model.expectShape(member.target) val resolveToNonPublicConstrainedType = targetShape !is StructureShape && targetShape !is UnionShape && !targetShape.hasTrait() && - (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) + (!publicConstrainedTypes || !targetShape.isDirectlyConstrained(symbolProvider)) val (unconstrainedVar, boxIt) = if (member.hasTrait()) { "(*unconstrained)" to ".map(Box::new).map_err(Box::new)" @@ -198,7 +198,7 @@ class UnconstrainedUnionGenerator( constrained.into() } """, - "ConstrainedSymbol" to constrainedSymbol + "ConstrainedSymbol" to constrainedSymbol, ) } else { rust( @@ -207,7 +207,7 @@ class UnconstrainedUnionGenerator( .try_into() $boxIt .map_err(Self::Error::${ConstraintViolation(member).name()})? - """ + """, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index c66ace2119..4ba6a98e29 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -19,7 +19,7 @@ class ServerRequestBindingGenerator( protocol: Protocol, coreCodegenContext: CoreCodegenContext, unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, - operationShape: OperationShape + operationShape: OperationShape, ) { private val httpBindingGenerator = HttpBindingGenerator(protocol, coreCodegenContext, unconstrainedShapeSymbolProvider, operationShape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index c70499540d..e07fe0cd30 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -49,8 +49,8 @@ import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.TypeConversionGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.smithy.generators.protocol.MakeOperationGenerator @@ -669,7 +669,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( protocol, codegenContext, unconstrainedShapeSymbolProvider, - operationShape + operationShape, ) val structuredDataParser = protocol.structuredDataParser(operationShape) Attribute.AllowUnusedMut.render(this) @@ -715,7 +715,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } }); } - """ + """, ) } } @@ -1034,7 +1034,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ let entry = query_params.entry(String::from(k)).or_default(); entry.push(String::from(v)); - """ + """, ) } } @@ -1053,21 +1053,23 @@ private class ServerHttpBoundProtocolTraitImplGenerator( // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not // implemented yet. val hasConstrainedTarget = - model.expectShape(binding.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) + model.expectShape(binding.member.target, CollectionShape::class.java).canReachConstrainedShape(model, symbolProvider) val memberName = unconstrainedShapeSymbolProvider.toMemberName(binding.member) val isOptional = unconstrainedShapeSymbolProvider.toSymbol(binding.member).isOptional() rustBlock("if !$memberName.is_empty()") { withBlock( "input = input.${ - binding.member.deserializerBuilderSetterName(codegenContext.target) - }(", ");" + binding.member.deserializerBuilderSetterName(codegenContext.target) + }(", + ");", ) { conditionalBlock("Some(", ")", conditional = isOptional) { conditionalBlock( "#T(", ")", conditional = hasConstrainedTarget, - unconstrainedShapeSymbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }) { + unconstrainedShapeSymbolProvider.toSymbol(binding.member).mapRustType { it.stripOuter() }, + ) { write(memberName) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 3476f41795..5339daaa07 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -38,13 +38,13 @@ val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( fun serverTestSymbolProvider( model: Model, serviceShape: ServiceShape? = null, - publicConstrainedTypesEnabled: Boolean = true + publicConstrainedTypesEnabled: Boolean = true, ): RustSymbolProvider = RustCodegenServerPlugin.baseSymbolProvider( model, serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), ServerTestSymbolVisitorConfig, - publicConstrainedTypes = publicConstrainedTypesEnabled + publicConstrainedTypes = publicConstrainedTypesEnabled, ) fun serverTestRustSettings( @@ -77,7 +77,7 @@ fun serverTestCodegenContext( model: Model, serviceShape: ServiceShape? = null, settings: ServerRustSettings = serverTestRustSettings(), - protocolShapeId: ShapeId? = null + protocolShapeId: ShapeId? = null, ): ServerCodegenContext { val service = serviceShape @@ -97,7 +97,7 @@ fun serverTestCodegenContext( settings, unconstrainedShapeSymbolProvider, constrainedShapeSymbolProvider, - constraintViolationSymbolProvider + constraintViolationSymbolProvider, ) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt index af7a010ec1..46e34ec91d 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -68,7 +68,7 @@ class PubCrateConstrainedShapeSymbolProviderTest { transitivelyConstrainedCollectionType shouldBe RustType.Opaque( "TransitivelyConstrainedCollectionConstrained", - "crate::constrained::transitively_constrained_collection_constrained" + "crate::constrained::transitively_constrained_collection_constrained", ) } @@ -80,7 +80,7 @@ class PubCrateConstrainedShapeSymbolProviderTest { transitivelyConstrainedMapType shouldBe RustType.Opaque( "TransitivelyConstrainedMapConstrained", - "crate::constrained::transitively_constrained_map_constrained" + "crate::constrained::transitively_constrained_map_constrained", ) } @@ -92,8 +92,8 @@ class PubCrateConstrainedShapeSymbolProviderTest { memberType shouldBe RustType.Option( RustType.Opaque( "TransitivelyConstrainedCollectionConstrained", - "crate::constrained::transitively_constrained_collection_constrained" - ) + "crate::constrained::transitively_constrained_collection_constrained", + ), ) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 39a0243c41..8b93e33e26 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -18,9 +18,9 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.Instantiator @@ -79,7 +79,7 @@ class ConstrainedMapGeneratorTest { } """.asSmithyModel(), it.second, - it.third + it.third, ) } @@ -105,7 +105,7 @@ class ConstrainedMapGeneratorTest { symbolProvider, constraintViolationSymbolProvider, writer, - constrainedMapShape + constrainedMapShape, ) val instantiator = @@ -122,21 +122,21 @@ class ConstrainedMapGeneratorTest { test = """ let map = build_valid_map(); let _constrained = ConstrainedMap::parse(map).unwrap(); - """ + """, ) writer.unitTest( name = "try_from_success", test = """ let map = build_valid_map(); let _constrained: ConstrainedMap = map.try_into().unwrap(); - """ + """, ) writer.unitTest( name = "parse_fail", test = """ let map = build_invalid_map(); let _constrained = ConstrainedMap::parse(map).unwrap_err(); - """ + """, ) writer.unitTest( name = "try_from_fail", @@ -144,7 +144,7 @@ class ConstrainedMapGeneratorTest { let map = build_invalid_map(); let constrained_res: Result = map.try_into(); constrained_res.unwrap_err(); - """ + """, ) writer.unitTest( name = "inner", @@ -153,7 +153,7 @@ class ConstrainedMapGeneratorTest { let constrained = ConstrainedMap::parse(map.clone()).unwrap(); assert_eq!(constrained.inner(), &map); - """ + """, ) writer.unitTest( name = "into_inner", @@ -162,7 +162,7 @@ class ConstrainedMapGeneratorTest { let constrained = ConstrainedMap::parse(map.clone()).unwrap(); assert_eq!(constrained.into_inner(), map); - """ + """, ) } @@ -193,7 +193,7 @@ class ConstrainedMapGeneratorTest { symbolProvider, constraintViolationSymbolProvider, writer, - constrainedMapShape + constrainedMapShape, ) // Check that the wrapped type is `pub(crate)`. @@ -205,14 +205,14 @@ class ConstrainedMapGeneratorTest { symbolProvider: RustSymbolProvider, constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, writer: RustWriter, - constrainedMapShape: MapShape + constrainedMapShape: MapShape, ) { ConstrainedMapGenerator( model, symbolProvider, constraintViolationSymbolProvider, writer, - constrainedMapShape + constrainedMapShape, ).render() MapConstraintViolationGenerator( @@ -220,7 +220,7 @@ class ConstrainedMapGeneratorTest { symbolProvider, constraintViolationSymbolProvider, writer, - constrainedMapShape + constrainedMapShape, ).render() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 68ca5cc324..a247ad6b63 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -16,8 +16,8 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -63,7 +63,7 @@ class ConstrainedStringGeneratorTest { "@length(min: 3, max: 3)", "👍👍👍", // These three emojis are three Unicode scalar values. "👍👍👍👍", - ) + ), ).map { TestCase( """ @@ -73,7 +73,7 @@ class ConstrainedStringGeneratorTest { string ConstrainedString """.asSmithyModel(), it.second, - it.third + it.third, ) } @@ -98,7 +98,7 @@ class ConstrainedStringGeneratorTest { symbolProvider, constraintViolationSymbolProvider, writer, - constrainedStringShape + constrainedStringShape, ).render() writer.unitTest( @@ -106,21 +106,21 @@ class ConstrainedStringGeneratorTest { test = """ let string = String::from("${testCase.validString}"); let _constrained = ConstrainedString::parse(string).unwrap(); - """ + """, ) writer.unitTest( name = "try_from_success", test = """ let string = String::from("${testCase.validString}"); let _constrained: ConstrainedString = string.try_into().unwrap(); - """ + """, ) writer.unitTest( name = "parse_fail", test = """ let string = String::from("${testCase.invalidString}"); let _constrained = ConstrainedString::parse(string).unwrap_err(); - """ + """, ) writer.unitTest( name = "try_from_fail", @@ -128,7 +128,7 @@ class ConstrainedStringGeneratorTest { let string = String::from("${testCase.invalidString}"); let constrained_res: Result = string.try_into(); constrained_res.unwrap_err(); - """ + """, ) writer.unitTest( name = "inner", @@ -137,7 +137,7 @@ class ConstrainedStringGeneratorTest { let constrained = ConstrainedString::parse(string).unwrap(); assert_eq!(constrained.inner(), "${testCase.validString}"); - """ + """, ) writer.unitTest( name = "into_inner", @@ -146,7 +146,7 @@ class ConstrainedStringGeneratorTest { let constrained = ConstrainedString::parse(string.clone()).unwrap(); assert_eq!(constrained.into_inner(), string); - """ + """, ) } @@ -174,7 +174,7 @@ class ConstrainedStringGeneratorTest { symbolProvider, constraintViolationSymbolProvider, writer, - constrainedStringShape + constrainedStringShape, ).render() // Check that the wrapped type is `pub(crate)`. diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 854cae22ff..574794c5eb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -9,8 +9,8 @@ import io.kotest.matchers.string.shouldNotContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest import software.amazon.smithy.rust.codegen.util.expectTrait @@ -51,7 +51,7 @@ class ServerEnumGeneratorTest { constraintViolationSymbolProvider, writer, shape, - shape.expectTrait() + shape.expectTrait(), ).render() writer.compileAndTest( """ @@ -59,7 +59,7 @@ class ServerEnumGeneratorTest { assert_eq!(InstanceType::try_from("t2.nano").unwrap(), InstanceType::T2Nano); assert_eq!(InstanceType::from_str("t2.nano").unwrap(), InstanceType::T2Nano); assert_eq!(InstanceType::try_from("unknown").unwrap_err(), crate::model::instance_type::ConstraintViolation(String::from("unknown"))); - """ + """, ) } @@ -71,7 +71,7 @@ class ServerEnumGeneratorTest { constraintViolationSymbolProvider, writer, shape, - shape.expectTrait() + shape.expectTrait(), ).render() writer.compileAndTest( """ @@ -93,7 +93,7 @@ class ServerEnumGeneratorTest { constraintViolationSymbolProvider, writer, shape, - shape.expectTrait() + shape.expectTrait(), ).render() writer.toString() shouldNotContain "#[non_exhaustive]" } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 5d813bf638..1a36e92dfc 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -10,9 +10,9 @@ import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -81,7 +81,7 @@ class UnconstrainedCollectionGeneratorTest { unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, writer, - it + it, ).render() } } @@ -97,7 +97,7 @@ class UnconstrainedCollectionGeneratorTest { constraintViolationSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, - it + it, ).render() } @@ -118,7 +118,7 @@ class UnconstrainedCollectionGeneratorTest { expected_err, crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -136,7 +136,7 @@ class UnconstrainedCollectionGeneratorTest { crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); assert_eq!(expected, actual); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -147,7 +147,7 @@ class UnconstrainedCollectionGeneratorTest { let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """ + """, ) project.compileAndTest() } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index ce454a3495..6f04935cc0 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -10,9 +10,9 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -83,7 +83,7 @@ class UnconstrainedMapGeneratorTest { unconstrainedShapeSymbolProvider, pubCrateConstrainedShapeSymbolProvider, writer, - it + it, ).render() } } @@ -99,7 +99,7 @@ class UnconstrainedMapGeneratorTest { pubCrateConstrainedShapeSymbolProvider, constraintViolationSymbolProvider, unconstrainedModuleWriter, - it + it, ).render() MapConstraintViolationGenerator( @@ -107,7 +107,7 @@ class UnconstrainedMapGeneratorTest { symbolProvider, constraintViolationSymbolProvider, modelsModuleWriter, - it + it, ).render() } @@ -141,7 +141,7 @@ class UnconstrainedMapGeneratorTest { let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); assert!(actual_err == missing_string_expected_err || actual_err == missing_int_expected_err); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -172,7 +172,7 @@ class UnconstrainedMapGeneratorTest { expected, crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -191,7 +191,7 @@ class UnconstrainedMapGeneratorTest { ); let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); - """ + """, ) project.compileAndTest() diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index 603649bc31..e21397fe9a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -10,9 +10,9 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustModule -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider @@ -81,7 +81,7 @@ class UnconstrainedUnionGeneratorTest { constraintViolationSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, - unionShape + unionShape, ).render() unconstrainedModuleWriter.unitTest( @@ -98,7 +98,7 @@ class UnconstrainedUnionGeneratorTest { expected_err, crate::model::Union::try_from(union_unconstrained).unwrap_err() ); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -113,7 +113,7 @@ class UnconstrainedUnionGeneratorTest { let actual: crate::model::Union = crate::model::Union::try_from(union_unconstrained).unwrap(); assert_eq!(expected, actual); - """ + """, ) unconstrainedModuleWriter.unitTest( @@ -124,7 +124,7 @@ class UnconstrainedUnionGeneratorTest { let _union: crate::constrained::MaybeConstrained = union_unconstrained.into(); - """ + """, ) project.compileAndTest() } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index 99a6ea397d..dd709cad4a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -165,7 +165,7 @@ val DefaultPublicModules = setOf( ModelsModule, InputsModule, OutputsModule, - ConfigModule + ConfigModule, ).associateBy { it.name } /** diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt index 64cda71d08..63124c5726 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ConstraintViolationSymbolProvider.kt @@ -65,9 +65,9 @@ class ConstraintViolationSymbolProvider( val symbol = base.toSymbol(shape) val constraintViolationNamespace = "${symbol.namespace.let { it.ifEmpty { "crate::${Models.namespace}" } }}::${ - RustReservedWords.escapeIfNeeded( - shape.contextName(serviceShape).toSnakeCase() - ) + RustReservedWords.escapeIfNeeded( + shape.contextName(serviceShape).toSnakeCase(), + ) }" val rustType = RustType.Opaque(constraintViolationName, constraintViolationNamespace) return Symbol.builder() @@ -99,9 +99,9 @@ class ConstraintViolationSymbolProvider( } is StringShape -> { val namespace = "crate::${Models.namespace}::${ - RustReservedWords.escapeIfNeeded( - shape.contextName(serviceShape).toSnakeCase() - ) + RustReservedWords.escapeIfNeeded( + shape.contextName(serviceShape).toSnakeCase(), + ) }" val rustType = RustType.Opaque(constraintViolationName, namespace) Symbol.builder() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt index c0044cba3f..e289781f09 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/Constraints.kt @@ -99,10 +99,10 @@ fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: Sym fun MemberShape.requiresNewtype() = // Note that member shapes whose only constraint trait is `required` do not require a newtype. this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() + this.hasTrait() || + // `uniqueItems` is deprecated, so we ignore it. + // this.hasTrait() || + this.hasTrait() fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider) = this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt index e58dd9af69..92c9e4a41f 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerRustSettings.kt @@ -78,7 +78,7 @@ data class ServerCodegenConfig( override val debugMode: Boolean = defaultDebugMode, override val eventStreamAllowList: Set = defaultEventStreamAllowList, // TODO Unit test that we don't generate public constrained types when this setting is false. - val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes + val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, eventStreamAllowList, ) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt index 587a230d25..6e9dd6de06 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt @@ -301,7 +301,8 @@ open class SymbolVisitor( } override fun operationShape(shape: OperationShape): Symbol { - return symbolBuilder(shape, + return symbolBuilder( + shape, RustType.Opaque( shape.contextName(serviceShape) .replaceFirstChar { it.uppercase() }, @@ -362,7 +363,7 @@ fun handleOptionality( symbol: Symbol, member: MemberShape, nullableIndex: NullableIndex, - config: SymbolVisitorConfig? = null + config: SymbolVisitorConfig? = null, ): Symbol { val handleRequired = config?.handleRequired ?: true return if (handleRequired && member.isRequired) { @@ -385,7 +386,6 @@ fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol = symbol.makeRustBoxed() } else symbol - fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder { val builder = Symbol.builder().putProperty(SHAPE_KEY, shape) return builder.rustType(rustType) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt index 6520fc2130..9066d3d8b1 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt @@ -16,7 +16,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolMap -import software.amazon.smithy.rust.codegen.util.PANIC import software.amazon.smithy.rust.codegen.util.deepMergeWith import java.util.ServiceLoader import java.util.logging.Logger diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt index 20eb647afe..662d02d2f2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt @@ -166,7 +166,7 @@ class Instantiator( conditional = when (codegenTarget) { CodegenTarget.CLIENT -> ctx.builder || symbol.isOptional() CodegenTarget.SERVER -> symbol.isOptional() - } + }, ) { writer.conditionalBlock( "Box::new(", diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt index 9ff7e1c805..3e818ca51d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ErrorGenerator.kt @@ -86,12 +86,12 @@ class ErrorGenerator( if (messageShape != null) { val messageSymbol = symbolProvider.toSymbol(messageShape).mapRustType { t -> t.asDeref() } val (returnType, message) = if (messageSymbol.rustType() - .stripOuter() is RustType.Opaque + .stripOuter() is RustType.Opaque ) { // The string shape has a constraint trait that makes its symbol be a wrapper tuple struct. if (messageSymbol.isOptional()) { "Option<&${ - messageSymbol.rustType().stripOuter().render() + messageSymbol.rustType().stripOuter().render() }>" to "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" } else { "&${messageSymbol.rustType().render()}" to "&self.${symbolProvider.toMemberName(messageShape)}" diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt index 18e066d5d3..742454a5c9 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/http/HttpBindingGenerator.kt @@ -94,7 +94,7 @@ class HttpBindingGenerator( private val protocol: Protocol, private val coreCodegenContext: CoreCodegenContext, private val symbolProvider: RustSymbolProvider, - private val operationShape: OperationShape + private val operationShape: OperationShape, ) { private val runtimeConfig = coreCodegenContext.runtimeConfig private val codegenTarget = coreCodegenContext.target @@ -354,16 +354,16 @@ class HttpBindingGenerator( } else if (coreShape.isPrimitive()) { rust( "let $parsedValue = #T::read_many_primitive::<${coreType.render()}>(headers)?;", - headerUtil + headerUtil, ) } else { check(coreShape.isStringShape) { "The `httpHeader` trait can be applied to structure members that target a `boolean`, `number`, `string`, or " + - "`timestamp`; or a `structure` member that targets a list/set of these types. Found $coreShape." + "`timestamp`; or a `structure` member that targets a list/set of these types. Found $coreShape." } rust( "let $parsedValue: Vec<${coreType.render()}> = #T::read_many_from_str(headers)?;", - headerUtil + headerUtil, ) if (coreShape.hasTrait()) { rustTemplate( @@ -404,8 +404,8 @@ class HttpBindingGenerator( ) else -> { val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && - targetShape is CollectionShape && - targetShape.canReachConstrainedShape(model, symbolProvider) + targetShape is CollectionShape && + targetShape.canReachConstrainedShape(model, symbolProvider) if (returnUnconstrainedType) { rust( """ @@ -415,7 +415,7 @@ class HttpBindingGenerator( None }) """, - symbolProvider.toSymbol(targetShape) + symbolProvider.toSymbol(targetShape), ) } else { rustTemplate( @@ -427,7 +427,7 @@ class HttpBindingGenerator( Ok($parsedValue.pop()) } """, - "header_util" to headerUtil + "header_util" to headerUtil, ) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt index c67ae9e421..dd511167b2 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt @@ -220,8 +220,9 @@ class JsonParserGenerator( CodegenTarget.CLIENT -> { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) - }(", ");" + member.deserializerBuilderSetterName(codegenTarget) + }(", + ");", ) { deserializeMember(member) } @@ -230,8 +231,9 @@ class JsonParserGenerator( if (symbolProvider.toSymbol(member).isOptional()) { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) - }(", ");" + member.deserializerBuilderSetterName(codegenTarget) + }(", + ");", ) { deserializeMember(member) } @@ -242,10 +244,10 @@ class JsonParserGenerator( """ { builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) - }(v); + member.deserializerBuilderSetterName(codegenTarget) + }(v); } - """ + """, ) } } @@ -275,7 +277,8 @@ class JsonParserGenerator( if (symbol.isRustBoxed()) { if (codegenTarget == CodegenTarget.SERVER && model.expectShape(memberShape.container).isStructureShape && - memberShape.targetCanReachConstrainedShape(model, symbolProvider)) { + memberShape.targetCanReachConstrainedShape(model, symbolProvider) + ) { // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. rust(".map(|x| x.into())") } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt index efb2927e14..00a59ba8b2 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/customizations/HttpVersionListGeneratorTest.kt @@ -186,24 +186,27 @@ internal class HttpVersionListGeneratorTest { val combinedCodegenDecorator: CombinedCodegenDecorator = CombinedCodegenDecorator.fromClasspath(ctx, RequiredCustomizations()) - .withDecorator(object : AddRustTestsDecorator("validate_eventstream_http", { - TokioTest.render(this) - rust( - """ - async fn test_http_version_list_defaults() { - let conf = $moduleName::Config::builder().build(); - let op = $moduleName::operation::SayHello::builder() - .build().expect("valid operation") - .make_operation(&conf).await.unwrap(); - let properties = op.properties(); - let actual_http_versions = properties.get::>() - .expect("http versions list should be in property bag"); - let expected_http_versions = &vec![http::Version::HTTP_2]; - assert_eq!(actual_http_versions, expected_http_versions); - } - """, - ) - },) { + .withDecorator(object : AddRustTestsDecorator( + "validate_eventstream_http", + { + TokioTest.render(this) + rust( + """ + async fn test_http_version_list_defaults() { + let conf = $moduleName::Config::builder().build(); + let op = $moduleName::operation::SayHello::builder() + .build().expect("valid operation") + .make_operation(&conf).await.unwrap(); + let properties = op.properties(); + let actual_http_versions = properties.get::>() + .expect("http versions list should be in property bag"); + let expected_http_versions = &vec![http::Version::HTTP_2]; + assert_eq!(actual_http_versions, expected_http_versions); + } + """, + ) + }, + ) { override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, From c31291042e11f39733779098467530fb6cd81b14 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 13 Sep 2022 17:34:20 +0200 Subject: [PATCH 175/255] Make spst compile --- .../smithy/PythonServerCodegenVisitor.kt | 30 ++++----- .../generators/PythonServerEnumGenerator.kt | 12 +--- .../server/smithy/ServerCodegenVisitor.kt | 64 +++++++++++-------- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 6aa94e239d..38c61fab12 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -23,9 +23,7 @@ import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget -import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.util.getTrait /** @@ -64,7 +62,16 @@ class PythonServerCodegenVisitor( // Override `codegenContext` which carries the symbolProvider. codegenContext = - ServerCodegenContext(model, symbolProvider, service, protocol, settings, unconstrainedShapeSymbolProvider) + ServerCodegenContext( + model, + symbolProvider, + service, + protocol, + settings, + unconstrainedShapeSymbolProvider, + constrainedShapeSymbolProvider, + constraintViolationSymbolProvider, + ) // Override `rustCrate` which carries the symbolProvider. rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) @@ -88,12 +95,8 @@ class PythonServerCodegenVisitor( // Use Python specific structure generator that adds the #[pyclass] attribute // and #[pymethods] implementation. PythonServerStructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - val builderGenerator = - BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) - builderGenerator.render(writer) - writer.implBlock(shape, symbolProvider) { - builderGenerator.renderConvenienceMethod(this) - } + + renderStructureShapeBuilder(shape, writer) } } @@ -106,14 +109,7 @@ class PythonServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an enum $shape") shape.getTrait()?.also { enum -> rustCrate.useShapeWriter(shape) { writer -> - PythonServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - shape, - enum, - ).render() + PythonServerEnumGenerator(codegenContext, writer, shape, enum).render() } } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index 0668ae3d2e..815ceec778 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators -import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute @@ -18,8 +17,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.util.dq /** @@ -28,16 +26,12 @@ import software.amazon.smithy.rust.codegen.util.dq * some utility functions like `__str__()` and `__repr__()`. */ class PythonServerEnumGenerator( - model: Model, - symbolProvider: RustSymbolProvider, - constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: StringShape, enumTrait: EnumTrait, ) : ServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, + codegenContext, writer, shape, enumTrait, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 1cfac48a02..54e0640af4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule +import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedTraitForEnumGenerator @@ -84,7 +85,7 @@ open class ServerCodegenVisitor( protected var symbolProvider: RustSymbolProvider protected val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider - private val constrainedShapeSymbolProvider: RustSymbolProvider + protected val constrainedShapeSymbolProvider: RustSymbolProvider protected val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider protected var rustCrate: RustCrate private val fileManifest = context.fileManifest @@ -240,38 +241,45 @@ open class ServerCodegenVisitor( rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) - if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) { - val serverBuilderGenerator = ServerBuilderGenerator( - codegenContext, - shape, - if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null, - ) - serverBuilderGenerator.render(writer) - - if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - writer.implBlock(shape, symbolProvider) { - serverBuilderGenerator.renderConvenienceMethod(this) - } + renderStructureShapeBuilder(shape, writer) + } + } + + protected fun renderStructureShapeBuilder( + shape: StructureShape, + writer: RustWriter, + ) { + if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) { + val serverBuilderGenerator = ServerBuilderGenerator( + codegenContext, + shape, + if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null, + ) + serverBuilderGenerator.render(writer) + + if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { + writer.implBlock(shape, symbolProvider) { + serverBuilderGenerator.renderConvenienceMethod(this) } } + } - if (shape.isReachableFromOperationInput()) { - ServerStructureConstrainedTraitImpl( - symbolProvider, - codegenContext.settings.codegenConfig.publicConstrainedTypes, - shape, - writer, - ).render() - } + if (shape.isReachableFromOperationInput()) { + ServerStructureConstrainedTraitImpl( + symbolProvider, + codegenContext.settings.codegenConfig.publicConstrainedTypes, + shape, + writer, + ).render() + } - if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { - val serverBuilderGeneratorWithoutPublicConstrainedTypes = - ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) - serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) + if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { + val serverBuilderGeneratorWithoutPublicConstrainedTypes = + ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) + serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) - writer.implBlock(shape, symbolProvider) { - serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) - } + writer.implBlock(shape, symbolProvider) { + serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) } } } From 860e272f27740ff23e7b6b6318f1699d1b8bba1e Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 09:07:53 +0200 Subject: [PATCH 176/255] Fix Python server; spst fully works --- codegen-server-test/model/simple.smithy | 9 +- .../smithy/PythonCodegenServerPlugin.kt | 4 +- .../smithy/PythonServerCodegenVisitor.kt | 34 +++++-- .../server/smithy/ServerCodegenVisitor.kt | 91 +++++++------------ .../server/smithy/ServerSymbolProviders.kt | 64 +++++++++++++ .../generators/ServerBuilderGenerator.kt | 7 +- .../smithy/testutil/ServerTestHelpers.kt | 36 +++++--- .../codegen/smithy/ServerCodegenContext.kt | 1 + 8 files changed, 156 insertions(+), 90 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt diff --git a/codegen-server-test/model/simple.smithy b/codegen-server-test/model/simple.smithy index 6e094abe1f..2a0a7deb77 100644 --- a/codegen-server-test/model/simple.smithy +++ b/codegen-server-test/model/simple.smithy @@ -126,9 +126,16 @@ structure StoreServiceBlobInput { @required @httpPayload content: Blob, + + @httpHeader("lengthString") + lengthString: LengthString, } @documentation("Store a blob for a service id output structure") structure StoreServiceBlobOutput { - + @required + lengthString: LengthString } + +@length(min: 1, max: 68) +string LengthString diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt index 63866be3ba..b54eb19a1c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt @@ -71,9 +71,7 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin { // `aws_smithy_http_server_python::types`. PythonServerSymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { - EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) - } + .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes .let { BaseSymbolMetadataProvider(it, model, additionalAttributes = listOf()) } // Streaming shapes need different derives (e.g. they cannot derive Eq) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 38c61fab12..5ea99fdf10 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy import software.amazon.smithy.build.PluginContext 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.StringShape import software.amazon.smithy.model.shapes.StructureShape @@ -17,6 +18,7 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.generators.Pytho import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerServiceGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.RustCrate @@ -57,24 +59,40 @@ class PythonServerCodegenVisitor( ) .protocolFor(context.model, service) protocolGeneratorFactory = generator + model = codegenDecorator.transformModel(service, baseModel) - symbolProvider = PythonCodegenServerPlugin.baseSymbolProvider(model, service, symbolVisitorConfig) - // Override `codegenContext` which carries the symbolProvider. + fun baseSymbolProviderFactory( + model: Model, + serviceShape: ServiceShape, + symbolVisitorConfig: SymbolVisitorConfig, + _publicConstrainedTypes: Boolean = true, + ) = PythonCodegenServerPlugin.baseSymbolProvider(model, serviceShape, symbolVisitorConfig) + + val serverSymbolProviders = ServerSymbolProviders.from( + model, + service, + symbolVisitorConfig, + settings.codegenConfig.publicConstrainedTypes, + ::baseSymbolProviderFactory, + ) + + // Override `codegenContext` which carries the various symbol providers. codegenContext = ServerCodegenContext( model, - symbolProvider, + serverSymbolProviders.symbolProvider, service, protocol, settings, - unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, - constraintViolationSymbolProvider, + serverSymbolProviders.unconstrainedShapeSymbolProvider, + serverSymbolProviders.constrainedShapeSymbolProvider, + serverSymbolProviders.constraintViolationSymbolProvider, + serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, ) // Override `rustCrate` which carries the symbolProvider. - rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) + rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, DefaultPublicModules, settings.codegenConfig) // Override `protocolGenerator` which carries the symbolProvider. protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } @@ -94,7 +112,7 @@ class PythonServerCodegenVisitor( rustCrate.useShapeWriter(shape) { writer -> // Use Python specific structure generator that adds the #[pyclass] attribute // and #[pymethods] implementation. - PythonServerStructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) + PythonServerStructureGenerator(model, codegenContext.symbolProvider, writer, shape).render(CodegenTarget.SERVER) renderStructureShapeBuilder(shape, writer) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 54e0640af4..5ca427423e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -38,17 +38,13 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader import software.amazon.smithy.rust.codegen.smithy.Constrained -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RustCrate -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.Unconstrained -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget @@ -82,11 +78,6 @@ open class ServerCodegenVisitor( protected val logger = Logger.getLogger(javaClass.name) protected val settings = ServerRustSettings.from(context.model, context.settings) - protected var symbolProvider: RustSymbolProvider - protected val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider - protected val constrainedShapeSymbolProvider: RustSymbolProvider - protected val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider protected var rustCrate: RustCrate private val fileManifest = context.fileManifest protected var model: Model @@ -120,44 +111,28 @@ open class ServerCodegenVisitor( protocolGeneratorFactory = generator model = codegenDecorator.transformModel(service, baseModel) - // TODO Can we make it work setting `publicConstrainedTypes` to `false`? - symbolProvider = RustCodegenServerPlugin.baseSymbolProvider( - model, - service, - symbolVisitorConfig, - publicConstrainedTypes = settings.codegenConfig.publicConstrainedTypes, - ) - constrainedShapeSymbolProvider = RustCodegenServerPlugin.baseSymbolProvider( + + val serverSymbolProviders = ServerSymbolProviders.from( model, service, symbolVisitorConfig, - publicConstrainedTypes = true, + settings.codegenConfig.publicConstrainedTypes, + RustCodegenServerPlugin::baseSymbolProvider ) - // TODO Shouldn't `UnconstrainedShapeSymbolProvider` be applied at the same level as the `ConstrainedShapeSymbolProvider`? - unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( - RustCodegenServerPlugin.baseSymbolProvider( - model, - service, - symbolVisitorConfig, - publicConstrainedTypes = false, - ), - model, service, settings.codegenConfig.publicConstrainedTypes, - ) - pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, service) - constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) codegenContext = ServerCodegenContext( model, - symbolProvider, + serverSymbolProviders.symbolProvider, service, protocol, settings, - unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, - constraintViolationSymbolProvider, + serverSymbolProviders.unconstrainedShapeSymbolProvider, + serverSymbolProviders.constrainedShapeSymbolProvider, + serverSymbolProviders.constraintViolationSymbolProvider, + serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, ) - rustCrate = RustCrate(context.fileManifest, symbolProvider, DefaultPublicModules, settings.codegenConfig) + rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) // TODO Traverse the model and error out if: @@ -239,7 +214,7 @@ open class ServerCodegenVisitor( override fun structureShape(shape: StructureShape) { logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { writer -> - StructureGenerator(model, symbolProvider, writer, shape).render(CodegenTarget.SERVER) + StructureGenerator(model, codegenContext.symbolProvider, writer, shape).render(CodegenTarget.SERVER) renderStructureShapeBuilder(shape, writer) } @@ -250,15 +225,11 @@ open class ServerCodegenVisitor( writer: RustWriter, ) { if (codegenContext.settings.codegenConfig.publicConstrainedTypes || shape.isReachableFromOperationInput()) { - val serverBuilderGenerator = ServerBuilderGenerator( - codegenContext, - shape, - if (shape.isReachableFromOperationInput()) pubCrateConstrainedShapeSymbolProvider else null, - ) + val serverBuilderGenerator = ServerBuilderGenerator(codegenContext, shape) serverBuilderGenerator.render(writer) if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - writer.implBlock(shape, symbolProvider) { + writer.implBlock(shape, codegenContext.symbolProvider) { serverBuilderGenerator.renderConvenienceMethod(this) } } @@ -266,7 +237,7 @@ open class ServerCodegenVisitor( if (shape.isReachableFromOperationInput()) { ServerStructureConstrainedTraitImpl( - symbolProvider, + codegenContext.symbolProvider, codegenContext.settings.codegenConfig.publicConstrainedTypes, shape, writer, @@ -278,7 +249,7 @@ open class ServerCodegenVisitor( ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) - writer.implBlock(shape, symbolProvider) { + writer.implBlock(shape, codegenContext.symbolProvider) { serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) } } @@ -290,7 +261,7 @@ open class ServerCodegenVisitor( private fun collectionShape(shape: CollectionShape) { if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider, + codegenContext.symbolProvider, ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for collection shape $shape") @@ -298,7 +269,7 @@ open class ServerCodegenVisitor( rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedCollectionGenerator( codegenContext, - pubCrateConstrainedShapeSymbolProvider, + codegenContext.pubCrateConstrainedShapeSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, shape, @@ -310,7 +281,7 @@ open class ServerCodegenVisitor( rustCrate.withModule(constrainedModule) { writer -> PubCrateConstrainedCollectionGenerator( codegenContext, - pubCrateConstrainedShapeSymbolProvider, + codegenContext.pubCrateConstrainedShapeSymbolProvider, writer, shape, ).render() @@ -322,26 +293,26 @@ open class ServerCodegenVisitor( val renderUnconstrainedMap = shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider, + codegenContext.symbolProvider, ) if (renderUnconstrainedMap) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> UnconstrainedMapGenerator( codegenContext, - pubCrateConstrainedShapeSymbolProvider, - unconstrainedShapeSymbolProvider, + codegenContext.pubCrateConstrainedShapeSymbolProvider, + codegenContext.unconstrainedShapeSymbolProvider, unconstrainedModuleWriter, shape, ).render() } - if (!shape.isDirectlyConstrained(symbolProvider)) { + if (!shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") rustCrate.withModule(constrainedModule) { writer -> PubCrateConstrainedMapGenerator( codegenContext, - pubCrateConstrainedShapeSymbolProvider, + codegenContext.pubCrateConstrainedShapeSymbolProvider, writer, shape, ).render() @@ -349,14 +320,14 @@ open class ServerCodegenVisitor( } } - val isDirectlyConstrained = shape.isDirectlyConstrained(symbolProvider) + val isDirectlyConstrained = shape.isDirectlyConstrained(codegenContext.symbolProvider) if (isDirectlyConstrained) { rustCrate.withModule(ModelsModule) { modelsModuleWriter -> ConstrainedMapGenerator( codegenContext, modelsModuleWriter, shape, - if (renderUnconstrainedMap) unconstrainedShapeSymbolProvider.toSymbol(shape) else null, + if (renderUnconstrainedMap) codegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape) else null, ).render() } } @@ -378,11 +349,11 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> ServerEnumGenerator(codegenContext, writer, shape, enum).render() - ConstrainedTraitForEnumGenerator(model, symbolProvider, writer, shape).render() + ConstrainedTraitForEnumGenerator(model, codegenContext.symbolProvider, writer, shape).render() } } - if (shape.hasTrait() && shape.isDirectlyConstrained(symbolProvider)) { + if (shape.hasTrait() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.warning( """ String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy @@ -391,7 +362,7 @@ open class ServerCodegenVisitor( See https://github.com/awslabs/smithy/issues/1121f for more information. """.trimIndent(), ) - } else if (shape.isDirectlyConstrained(symbolProvider)) { + } else if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> ConstrainedStringGenerator(codegenContext, writer, shape).render() @@ -409,12 +380,12 @@ open class ServerCodegenVisitor( override fun unionShape(shape: UnionShape) { logger.info("[rust-server-codegen] Generating an union shape $shape") rustCrate.useShapeWriter(shape) { - UnionGenerator(model, symbolProvider, it, shape, renderUnknownVariant = false).render() + UnionGenerator(model, codegenContext.symbolProvider, it, shape, renderUnknownVariant = false).render() } if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( model, - symbolProvider, + codegenContext.symbolProvider, ) ) { logger.info("[rust-server-codegen] Generating an unconstrained type for union shape $shape") @@ -422,7 +393,7 @@ open class ServerCodegenVisitor( rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedUnionGenerator( codegenContext, - pubCrateConstrainedShapeSymbolProvider, + codegenContext.pubCrateConstrainedShapeSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, shape, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt new file mode 100644 index 0000000000..4589dee272 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider + +/** + * Just a handy class to centralize initialization all the symbol providers required by the server code generators, to + * make the init blocks of the codegen visitors ([ServerCodegenVisitor] and [PythonServerCodegenVisitor]), and the + * unit test setup code, shorter and DRYer. + */ +class ServerSymbolProviders private constructor( + val symbolProvider: RustSymbolProvider, + val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + val constrainedShapeSymbolProvider: RustSymbolProvider, + val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, +) { + companion object { + fun from( + model: Model, + service: ServiceShape, + symbolVisitorConfig: SymbolVisitorConfig, + publicConstrainedTypes: Boolean, + baseSymbolProviderFactory: (model: Model, service: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, publicConstrainedTypes: Boolean) -> RustSymbolProvider, + ): ServerSymbolProviders { + val baseSymbolProvider = baseSymbolProviderFactory(model, service, symbolVisitorConfig, publicConstrainedTypes) + return ServerSymbolProviders( + symbolProvider = baseSymbolProvider, + constrainedShapeSymbolProvider = baseSymbolProviderFactory( + model, + service, + symbolVisitorConfig, + true, + ), + // TODO Shouldn't `UnconstrainedShapeSymbolProvider` be applied at the same level as the `ConstrainedShapeSymbolProvider`? + unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( + baseSymbolProviderFactory( + model, + service, + symbolVisitorConfig, + false, + ), + model, service, publicConstrainedTypes, + ), + pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider( + baseSymbolProvider, + model, + service, + ), + constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(baseSymbolProvider, model, service), + ) + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 979fa2b4c8..fb36fa59e9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -55,6 +55,7 @@ import software.amazon.smithy.rust.codegen.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled import software.amazon.smithy.rust.codegen.util.hasTrait +import software.amazon.smithy.rust.codegen.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.util.toPascalCase import software.amazon.smithy.rust.codegen.util.toSnakeCase @@ -68,9 +69,8 @@ import software.amazon.smithy.rust.codegen.util.toSnakeCase class ServerBuilderGenerator( codegenContext: ServerCodegenContext, private val shape: StructureShape, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider? = null, ) { - private val takeInUnconstrainedTypes = pubCrateConstrainedShapeSymbolProvider != null + private val takeInUnconstrainedTypes = shape.isReachableFromOperationInput() private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val symbolProvider = codegenContext.symbolProvider @@ -83,6 +83,7 @@ class ServerBuilderGenerator( } } private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) @@ -460,7 +461,7 @@ class ServerBuilderGenerator( val strippedOption = if (member.hasConstraintTraitOrTargetHasConstraintTrait(model, symbolProvider)) { constrainedShapeSymbolProvider.toSymbol(member) } else { - pubCrateConstrainedShapeSymbolProvider!!.toSymbol(member) + pubCrateConstrainedShapeSymbolProvider.toSymbol(member) } // Strip the `Option` in case the member is not `required`. .mapRustType { it.stripOuter() } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 5339daaa07..9f19f4f109 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -12,16 +12,14 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin +import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock @@ -83,21 +81,32 @@ fun serverTestCodegenContext( serviceShape ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build() - val symbolProvider = serverTestSymbolProvider(model, serviceShape) - val unconstrainedShapeSymbolProvider = - UnconstrainedShapeSymbolProvider(symbolProvider, model, service, settings.codegenConfig.publicConstrainedTypes) - val constrainedShapeSymbolProvider = serverTestSymbolProvider(model, publicConstrainedTypesEnabled = true) - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, service) val protocol = protocolShapeId ?: ShapeId.from("test#Protocol") + val symbolVisitorConfig = + SymbolVisitorConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = false, + handleRequired = true, + handleRustBoxing = true, + ) + val serverSymbolProviders = ServerSymbolProviders.from( + model, + service, + symbolVisitorConfig, + settings.codegenConfig.publicConstrainedTypes, + RustCodegenServerPlugin::baseSymbolProvider + ) + return ServerCodegenContext( model, - symbolProvider, + serverSymbolProviders.symbolProvider, service, protocol, settings, - unconstrainedShapeSymbolProvider, - constrainedShapeSymbolProvider, - constraintViolationSymbolProvider, + serverSymbolProviders.unconstrainedShapeSymbolProvider, + serverSymbolProviders.constrainedShapeSymbolProvider, + serverSymbolProviders.constraintViolationSymbolProvider, + serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, ) } @@ -107,12 +116,9 @@ fun serverTestCodegenContext( fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { StructureGenerator(model, symbolProvider, writer, this).render(CodegenTarget.SERVER) val serverCodegenContext = serverTestCodegenContext(model) - val pubCrateConstrainedShapeSymbolProvider = - PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serverCodegenContext.serviceShape) val modelBuilder = ServerBuilderGenerator( serverCodegenContext, this, - pubCrateConstrainedShapeSymbolProvider, ) modelBuilder.render(writer) writer.implBlock(this, symbolProvider) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt index 80b702f897..296b7be773 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/ServerCodegenContext.kt @@ -28,6 +28,7 @@ data class ServerCodegenContext( val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, val constrainedShapeSymbolProvider: RustSymbolProvider, val constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, ) : CoreCodegenContext( model, symbolProvider, serviceShape, protocol, settings, CodegenTarget.SERVER, ) From e8c42fdcbfd97d1d2c0d6aa118bdb3acfc51bce9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 10:12:38 +0200 Subject: [PATCH 177/255] Make server-unit-tests compile; no pass yet --- .../server/smithy/ServerCodegenVisitor.kt | 31 ++---------- .../PubCrateConstrainedCollectionGenerator.kt | 3 +- .../PubCrateConstrainedMapGenerator.kt | 3 +- .../UnconstrainedCollectionGenerator.kt | 3 +- .../generators/UnconstrainedMapGenerator.kt | 6 +-- .../generators/UnconstrainedUnionGenerator.kt | 3 +- .../smithy/testutil/ServerTestHelpers.kt | 28 +++++------ ...CrateConstrainedShapeSymbolProviderTest.kt | 10 ++-- .../UnconstrainedShapeSymbolProviderTest.kt | 18 ++++--- .../generators/ConstrainedMapGeneratorTest.kt | 47 +++---------------- .../ConstrainedStringGeneratorTest.kt | 29 +++--------- .../generators/ServerEnumGeneratorTest.kt | 31 ++---------- .../UnconstrainedCollectionGeneratorTest.kt | 33 ++----------- .../UnconstrainedMapGeneratorTest.kt | 43 +++-------------- .../UnconstrainedUnionGeneratorTest.kt | 25 ++-------- .../EventStreamUnmarshallerGeneratorTest.kt | 11 +---- 16 files changed, 67 insertions(+), 257 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 5ca427423e..2b93667475 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -269,7 +269,6 @@ open class ServerCodegenVisitor( rustCrate.withModule(ModelsModule) { modelsModuleWriter -> UnconstrainedCollectionGenerator( codegenContext, - codegenContext.pubCrateConstrainedShapeSymbolProvider, unconstrainedModuleWriter, modelsModuleWriter, shape, @@ -279,12 +278,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating a constrained type for collection shape $shape") rustCrate.withModule(constrainedModule) { writer -> - PubCrateConstrainedCollectionGenerator( - codegenContext, - codegenContext.pubCrateConstrainedShapeSymbolProvider, - writer, - shape, - ).render() + PubCrateConstrainedCollectionGenerator(codegenContext, writer, shape).render() } } } @@ -298,24 +292,13 @@ open class ServerCodegenVisitor( if (renderUnconstrainedMap) { logger.info("[rust-server-codegen] Generating an unconstrained type for map $shape") rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> - UnconstrainedMapGenerator( - codegenContext, - codegenContext.pubCrateConstrainedShapeSymbolProvider, - codegenContext.unconstrainedShapeSymbolProvider, - unconstrainedModuleWriter, - shape, - ).render() + UnconstrainedMapGenerator(codegenContext, unconstrainedModuleWriter, shape).render() } if (!shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained type for map $shape") rustCrate.withModule(constrainedModule) { writer -> - PubCrateConstrainedMapGenerator( - codegenContext, - codegenContext.pubCrateConstrainedShapeSymbolProvider, - writer, - shape, - ).render() + PubCrateConstrainedMapGenerator(codegenContext, writer, shape).render() } } } @@ -391,13 +374,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an unconstrained type for union shape $shape") rustCrate.withModule(unconstrainedModule) { unconstrainedModuleWriter -> rustCrate.withModule(ModelsModule) { modelsModuleWriter -> - UnconstrainedUnionGenerator( - codegenContext, - codegenContext.pubCrateConstrainedShapeSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - shape, - ).render() + UnconstrainedUnionGenerator(codegenContext, unconstrainedModuleWriter, modelsModuleWriter, shape).render() } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index ca038a87d8..81de31241d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -37,13 +36,13 @@ import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyCo */ class PubCrateConstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: CollectionShape, ) { private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val symbolProvider = codegenContext.symbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 16d5093f96..0ca7afe9f4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -12,7 +12,6 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -36,13 +35,13 @@ import software.amazon.smithy.rust.codegen.smithy.isTransitivelyButNotDirectlyCo */ class PubCrateConstrainedMapGenerator( val codegenContext: ServerCodegenContext, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, val writer: RustWriter, val shape: MapShape, ) { private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val symbolProvider = codegenContext.symbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 93736e7cb1..a0b81ea065 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape @@ -20,7 +19,6 @@ import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs class UnconstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: CollectionShape, @@ -28,6 +26,7 @@ class UnconstrainedCollectionGenerator( private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = with(codegenContext.constraintViolationSymbolProvider) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 9aec20b988..b52a221f4d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -14,9 +14,7 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained @@ -24,13 +22,13 @@ import software.amazon.smithy.rust.codegen.smithy.makeMaybeConstrained // TODO Docs class UnconstrainedMapGenerator( val codegenContext: ServerCodegenContext, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, - private val unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, val shape: MapShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val symbol = unconstrainedShapeSymbolProvider.toSymbol(shape) private val name = symbol.name private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 20ef404c4d..00c1cee7f3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -20,7 +20,6 @@ import software.amazon.smithy.rust.codegen.rustlang.withBlock import software.amazon.smithy.rust.codegen.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext @@ -36,13 +35,13 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase // TODO Docs class UnconstrainedUnionGenerator( val codegenContext: ServerCodegenContext, - private val pubCrateConstrainedShapeSymbolProvider: PubCrateConstrainedShapeSymbolProvider, private val unconstrainedModuleWriter: RustWriter, private val modelsModuleWriter: RustWriter, val shape: UnionShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val unconstrainedShapeSymbolProvider = codegenContext.unconstrainedShapeSymbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 9f19f4f109..68e6c2e5d7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -33,16 +33,19 @@ val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( handleRequired = true, ) -fun serverTestSymbolProvider( - model: Model, - serviceShape: ServiceShape? = null, - publicConstrainedTypesEnabled: Boolean = true, -): RustSymbolProvider = - RustCodegenServerPlugin.baseSymbolProvider( +private fun testServiceShapeFor(model: Model) = + model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build() + +fun serverTestSymbolProvider(model: Model, serviceShape: ServiceShape? = null) = + serverTestSymbolProviders(model, serviceShape).symbolProvider + +fun serverTestSymbolProviders(model: Model, serviceShape: ServiceShape? = null) = + ServerSymbolProviders.from( model, - serviceShape ?: ServiceShape.builder().version("test").id("test#Service").build(), + serviceShape ?: testServiceShapeFor(model), ServerTestSymbolVisitorConfig, - publicConstrainedTypes = publicConstrainedTypesEnabled, + serverTestRustSettings((serviceShape ?: testServiceShapeFor(model)).id).codegenConfig.publicConstrainedTypes, + RustCodegenServerPlugin::baseSymbolProvider, ) fun serverTestRustSettings( @@ -82,17 +85,10 @@ fun serverTestCodegenContext( ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build() val protocol = protocolShapeId ?: ShapeId.from("test#Protocol") - val symbolVisitorConfig = - SymbolVisitorConfig( - runtimeConfig = settings.runtimeConfig, - renameExceptions = false, - handleRequired = true, - handleRustBoxing = true, - ) val serverSymbolProviders = ServerSymbolProviders.from( model, service, - symbolVisitorConfig, + ServerTestSymbolVisitorConfig, settings.codegenConfig.publicConstrainedTypes, RustCodegenServerPlugin::baseSymbolProvider ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt index 46e34ec91d..302bb27ec6 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -11,13 +11,11 @@ import org.junit.jupiter.api.Test 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.ServiceShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustType -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProviders import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.util.lookup @@ -44,9 +42,9 @@ class PubCrateConstrainedShapeSymbolProviderTest { } """.asSmithyModel() - private val serviceShape = model.lookup("test#TestService") - private val symbolProvider = serverTestSymbolProvider(model, serviceShape, publicConstrainedTypesEnabled = false) - private val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) + private val serverTestSymbolProviders = serverTestSymbolProviders(model) + private val symbolProvider = serverTestSymbolProviders.symbolProvider + private val pubCrateConstrainedShapeSymbolProvider = serverTestSymbolProviders.pubCrateConstrainedShapeSymbolProvider @Test fun `it should crash when provided with a shape that is directly constrained`() { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index 871b39c86a..b508d6c654 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -8,11 +8,11 @@ package software.amazon.smithy.rust.codegen.server.smithy import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.render import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProviders import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -62,17 +62,16 @@ class UnconstrainedShapeSymbolProviderTest { } """.asSmithyModel() - val serviceShape = model.lookup("test#TestService") - val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) + val unconstrainedShapeSymbolProvider = serverTestSymbolProviders(model).unconstrainedShapeSymbolProvider val listAShape = model.lookup("test#ListA") - val listAType = symbolProvider.toSymbol(listAShape).rustType() + val listAType = unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType() val listBShape = model.lookup("test#ListB") - val listBType = symbolProvider.toSymbol(listBShape).rustType() + val listBType = unconstrainedShapeSymbolProvider.toSymbol(listBShape).rustType() val structureCShape = model.lookup("test#StructureC") - val structureCType = symbolProvider.toSymbol(structureCShape).rustType() + val structureCType = unconstrainedShapeSymbolProvider.toSymbol(structureCShape).rustType() listAType shouldBe RustType.Opaque("ListAUnconstrained", "crate::unconstrained::list_a_unconstrained") listBType shouldBe RustType.Opaque("ListBUnconstrained", "crate::unconstrained::list_b_unconstrained") @@ -94,13 +93,12 @@ class UnconstrainedShapeSymbolProviderTest { } """.asSmithyModel() - val serviceShape = model.lookup("test#TestService") - val symbolProvider = UnconstrainedShapeSymbolProvider(serverTestSymbolProvider(model, serviceShape), model, serviceShape) + val unconstrainedShapeSymbolProvider = serverTestSymbolProviders(model).unconstrainedShapeSymbolProvider val listAShape = model.lookup("test#ListA") val structureBShape = model.lookup("test#StructureB") - symbolProvider.toSymbol(structureBShape).rustType().render() shouldBe "crate::model::StructureB" - symbolProvider.toSymbol(listAShape).rustType().render() shouldBe "std::vec::Vec" + unconstrainedShapeSymbolProvider.toSymbol(structureBShape).rustType().render() shouldBe "crate::model::StructureB" + unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType().render() shouldBe "std::vec::Vec" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 8b93e33e26..f62fd598b3 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -19,10 +19,8 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -95,18 +93,11 @@ class ConstrainedMapGeneratorTest { val codegenContext = serverTestCodegenContext(testCase.model, serviceShape) val symbolProvider = codegenContext.symbolProvider - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, testCase.model, serviceShape) val project = TestWorkspace.testProject(symbolProvider) project.withModule(ModelsModule) { writer -> - render( - testCase.model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedMapShape, - ) + render(codegenContext, writer, constrainedMapShape) val instantiator = Instantiator(symbolProvider, testCase.model, codegenContext.runtimeConfig, codegenContext.target) @@ -180,47 +171,23 @@ class ConstrainedMapGeneratorTest { value: String } """.asSmithyModel() - val serviceShape = model.lookup("test#TestService") val constrainedMapShape = model.lookup("test#ConstrainedMap") - val symbolProvider = serverTestSymbolProvider(model) - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - val writer = RustWriter.forModule(ModelsModule.name) - render( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedMapShape, - ) + val codegenContext = serverTestCodegenContext(model) + render(codegenContext, writer, constrainedMapShape) // Check that the wrapped type is `pub(crate)`. writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) std::collections::HashMap);" } private fun render( - model: Model, - symbolProvider: RustSymbolProvider, - constraintViolationSymbolProvider: ConstraintViolationSymbolProvider, + codegenContext: ServerCodegenContext, writer: RustWriter, constrainedMapShape: MapShape, ) { - ConstrainedMapGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedMapShape, - ).render() - - MapConstraintViolationGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedMapShape, - ).render() + ConstrainedMapGenerator(codegenContext, writer, constrainedMapShape).render() + MapConstraintViolationGenerator(codegenContext, writer, constrainedMapShape).render() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index a247ad6b63..8ee64a71cf 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -13,11 +13,9 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.smithy.ModelsModule import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -84,22 +82,15 @@ class ConstrainedStringGeneratorTest { @ParameterizedTest @ArgumentsSource(ConstrainedStringGeneratorTestProvider::class) fun `it should generate constrained string types`(testCase: TestCase) { - val serviceShape = testCase.model.lookup("test#TestService") val constrainedStringShape = testCase.model.lookup("test#ConstrainedString") - val symbolProvider = serverTestSymbolProvider(testCase.model) - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, testCase.model, serviceShape) + val codegenContext = serverTestCodegenContext(testCase.model) + val symbolProvider = codegenContext.symbolProvider val project = TestWorkspace.testProject(symbolProvider) project.withModule(ModelsModule) { writer -> - ConstrainedStringGenerator( - testCase.model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedStringShape, - ).render() + ConstrainedStringGenerator(codegenContext, writer, constrainedStringShape).render() writer.unitTest( name = "parse_success", @@ -161,21 +152,13 @@ class ConstrainedStringGeneratorTest { @length(min: 1, max: 69) string ConstrainedString """.asSmithyModel() - val serviceShape = model.lookup("test#TestService") val constrainedStringShape = model.lookup("test#ConstrainedString") - val symbolProvider = serverTestSymbolProvider(model) - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) + val codegenContext = serverTestCodegenContext(model) val writer = RustWriter.forModule(ModelsModule.name) - ConstrainedStringGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - constrainedStringShape, - ).render() + ConstrainedStringGenerator(codegenContext, writer, constrainedStringShape).render() // Check that the wrapped type is `pub(crate)`. writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 574794c5eb..8048bf6a44 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest import software.amazon.smithy.rust.codegen.util.expectTrait @@ -37,22 +36,12 @@ class ServerEnumGeneratorTest { """.asSmithyModel() private val codegenContext = serverTestCodegenContext(model) - private val symbolProvider = codegenContext.symbolProvider - private val serviceShape = codegenContext.serviceShape - private val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) private val writer = RustWriter.forModule("model") private val shape = model.lookup("test#InstanceType") @Test fun `it generates TryFrom, FromStr and errors for enums`() { - ServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - shape, - shape.expectTrait(), - ).render() + ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() writer.compileAndTest( """ use std::str::FromStr; @@ -65,14 +54,7 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without the unknown variant`() { - ServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - shape, - shape.expectTrait(), - ).render() + ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() writer.compileAndTest( """ // check no unknown @@ -87,14 +69,7 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without non_exhaustive`() { - ServerEnumGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - writer, - shape, - shape.expectTrait(), - ).render() + ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() writer.toString() shouldNotContain "#[non_exhaustive]" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 1a36e92dfc..28cd5ff6b1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -7,15 +7,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest @@ -59,9 +55,9 @@ class UnconstrainedCollectionGeneratorTest { string: String } """.asSmithyModel() - val symbolProvider = serverTestSymbolProvider(model) + val codegenContext = serverTestCodegenContext(model) + val symbolProvider = codegenContext.symbolProvider - val serviceShape = model.lookup("test#TestService") val listA = model.lookup("test#ListA") val listB = model.lookup("test#ListB") @@ -71,34 +67,15 @@ class UnconstrainedCollectionGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(listA, listB).forEach { - PubCrateConstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - writer, - it, - ).render() + PubCrateConstrainedCollectionGenerator(codegenContext, writer, it).render() } } project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> project.withModule(ModelsModule) { modelsModuleWriter -> - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - it, - ).render() + UnconstrainedCollectionGenerator(codegenContext, unconstrainedModuleWriter, modelsModuleWriter, it).render() } unconstrainedModuleWriter.unitTest( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 6f04935cc0..40839109cb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -7,15 +7,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest @@ -61,9 +57,9 @@ class UnconstrainedMapGeneratorTest { string: String } """.asSmithyModel() - val symbolProvider = serverTestSymbolProvider(model) + val codegenContext = serverTestCodegenContext(model) + val symbolProvider = codegenContext.symbolProvider - val serviceShape = model.lookup("test#TestService") val mapA = model.lookup("test#MapA") val mapB = model.lookup("test#MapB") @@ -73,42 +69,17 @@ class UnconstrainedMapGeneratorTest { model.lookup("test#StructureC").serverRenderWithModelBuilder(model, symbolProvider, writer) } - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(RustModule.private("constrained")) { writer -> listOf(mapA, mapB).forEach { - PubCrateConstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - writer, - it, - ).render() + PubCrateConstrainedMapGenerator(codegenContext, writer, it).render() } } project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> project.withModule(ModelsModule) { modelsModuleWriter -> - val constraintViolationSymbolProvider = - ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) listOf(mapA, mapB).forEach { - UnconstrainedMapGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - it, - ).render() - - MapConstraintViolationGenerator( - model, - symbolProvider, - constraintViolationSymbolProvider, - modelsModuleWriter, - it, - ).render() + UnconstrainedMapGenerator(codegenContext, unconstrainedModuleWriter, it).render() + + MapConstraintViolationGenerator(codegenContext, modelsModuleWriter, it).render() } unconstrainedModuleWriter.unitTest( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index e21397fe9a..d0deebd191 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -6,16 +6,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.UnconstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -53,9 +49,9 @@ class UnconstrainedUnionGeneratorTest { requiredMember: String } """.asSmithyModel() - val symbolProvider = serverTestSymbolProvider(model) + val codegenContext = serverTestCodegenContext(model) + val symbolProvider = codegenContext.symbolProvider - val serviceShape = model.lookup("test#TestService") val unionShape = model.lookup("test#Union") val project = TestWorkspace.testProject(symbolProvider) @@ -64,25 +60,12 @@ class UnconstrainedUnionGeneratorTest { model.lookup("test#Structure").serverRenderWithModelBuilder(model, symbolProvider, writer) } - val unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) project.withModule(ModelsModule) { writer -> UnionGenerator(model, symbolProvider, writer, unionShape, renderUnknownVariant = false).render() } project.withModule(RustModule.private("unconstrained")) { unconstrainedModuleWriter -> project.withModule(ModelsModule) { modelsModuleWriter -> - val pubCrateConstrainedShapeSymbolProvider = PubCrateConstrainedShapeSymbolProvider(symbolProvider, model, serviceShape) - val constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(symbolProvider, model, serviceShape) - - UnconstrainedUnionGenerator( - model, - symbolProvider, - unconstrainedShapeSymbolProvider, - pubCrateConstrainedShapeSymbolProvider, - constraintViolationSymbolProvider, - unconstrainedModuleWriter, - modelsModuleWriter, - unionShape, - ).render() + UnconstrainedUnionGenerator(codegenContext, unconstrainedModuleWriter, modelsModuleWriter, unionShape).render() unconstrainedModuleWriter.unitTest( name = "unconstrained_union_fail_to_constrain", diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt index f5f28a7955..d2d80f230f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestModels import software.amazon.smithy.rust.codegen.smithy.protocols.EventStreamTestTools import software.amazon.smithy.rust.codegen.smithy.protocols.parse.EventStreamUnmarshallerGenerator -import software.amazon.smithy.rust.codegen.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.testutil.compileAndTest import software.amazon.smithy.rust.codegen.testutil.testRustSettings import software.amazon.smithy.rust.codegen.testutil.unitTest @@ -34,15 +33,7 @@ class EventStreamUnmarshallerGeneratorTest { target = testCase.target, ) val protocol = testCase.protocolBuilder(codegenContext) - val generator = EventStreamUnmarshallerGenerator( - protocol, - test.model, - TestRuntimeConfig, - test.symbolProvider, - test.operationShape, - test.streamShape, - target = testCase.target, - ) + val generator = EventStreamUnmarshallerGenerator(protocol, codegenContext, test.operationShape, test.streamShape) test.project.lib { writer -> writer.rust( From f8a323bbe69e1dfee147ecc4f39e9528a8cda62b Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 10:13:03 +0200 Subject: [PATCH 178/255] Fix 'it should generate unconstrained unions' test --- .../server/smithy/generators/UnconstrainedUnionGeneratorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index d0deebd191..005ab43c00 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -73,7 +73,7 @@ class UnconstrainedUnionGeneratorTest { let builder = crate::model::Structure::builder(); let union_unconstrained = union_unconstrained::UnionUnconstrained::Structure(builder); - let expected_err = crate::model::union::ConstraintViolation::StructureConstraintViolation( + let expected_err = crate::model::union::ConstraintViolation::Structure( crate::model::structure::ConstraintViolation::MissingRequiredMember, ); From 0f799a491b87c0d55e6ce2fd6864019aee292e81 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 11:12:30 +0200 Subject: [PATCH 179/255] Missing to `String` conversion in EventStreamUnmarshallerGenerator; server-unit-tests pass --- .../smithy/rust/codegen/smithy/generators/StructureGenerator.kt | 1 + .../smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index 314bfc1c82..09adbe9a56 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -61,6 +61,7 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S */ fun MemberShape.deserializerBuilderSetterName(codegenTarget: CodegenTarget) = when (codegenTarget) { + // TODO Both these arms return the same result. CodegenTarget.CLIENT -> this.setterName() CodegenTarget.SERVER -> "set_${this.memberName.toSnakeCase()}" } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index adbcd84117..28e7017e59 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -273,6 +273,7 @@ class EventStreamUnmarshallerGenerator( """ std::str::from_utf8(message.payload()) .map_err(|_| #{Error}::Unmarshalling("message payload is not valid UTF-8".into()))? + .to_owned() """, *codegenScope, ) From 33e20d84f82b3b808d6434b4c450b61e5f773b3a Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 14:31:17 +0200 Subject: [PATCH 180/255] =?UTF-8?q?Fix=20Python=20Pok=C3=A9mon=20server=20?= =?UTF-8?q?SDK=20generation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../smithy/PythonCodegenServerPlugin.kt | 4 +++ .../smithy/PythonServerCodegenVisitor.kt | 18 ++++++------- .../generators/PythonServerEnumGenerator.kt | 14 +---------- .../server/smithy/RustCodegenServerPlugin.kt | 8 +----- .../server/smithy/ServerCodegenVisitor.kt | 25 +++++++++++++------ .../smithy/generators/ServerEnumGenerator.kt | 5 ++-- .../generators/ServerEnumGeneratorTest.kt | 7 +++--- 7 files changed, 37 insertions(+), 44 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt index b54eb19a1c..7e69a07529 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustReservedWordSymbolProvid import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerSymbolVisitor import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonStreamingShapeMetadataProvider +import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations import software.amazon.smithy.rust.codegen.smithy.BaseSymbolMetadataProvider import software.amazon.smithy.rust.codegen.smithy.EventStreamSymbolProvider @@ -66,10 +67,13 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, + publicConstrainedTypes: Boolean = true, ) = // Rename a set of symbols that do not implement `PyClass` and have been wrapped in // `aws_smithy_http_server_python::types`. PythonServerSymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) + // TODO Docs + .let { if (publicConstrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 5ea99fdf10..df0ea9e2e7 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerServiceGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator @@ -26,7 +27,6 @@ import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget -import software.amazon.smithy.rust.codegen.util.getTrait /** * Entrypoint for Python server-side code generation. This class will walk the in-memory model and @@ -62,12 +62,15 @@ class PythonServerCodegenVisitor( model = codegenDecorator.transformModel(service, baseModel) + // `publicConstrainedTypes` must always be `false` for the Python server. + settings = settings.copy(codegenConfig = settings.codegenConfig.copy(publicConstrainedTypes = false)) + fun baseSymbolProviderFactory( model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - _publicConstrainedTypes: Boolean = true, - ) = PythonCodegenServerPlugin.baseSymbolProvider(model, serviceShape, symbolVisitorConfig) + publicConstrainedTypes: Boolean + ) = PythonCodegenServerPlugin.baseSymbolProvider(model, serviceShape, symbolVisitorConfig, publicConstrainedTypes) val serverSymbolProviders = ServerSymbolProviders.from( model, @@ -124,12 +127,9 @@ class PythonServerCodegenVisitor( * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - logger.info("[rust-server-codegen] Generating an enum $shape") - shape.getTrait()?.also { enum -> - rustCrate.useShapeWriter(shape) { writer -> - PythonServerEnumGenerator(codegenContext, writer, shape, enum).render() - } - } + fun pythonServerEnumGeneratorFactory(codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) = + PythonServerEnumGenerator(codegenContext, writer, shape) + stringShape(shape, ::pythonServerEnumGeneratorFactory) } /** diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index 815ceec778..8f3907bac1 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Writable @@ -29,13 +28,7 @@ class PythonServerEnumGenerator( codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: StringShape, - enumTrait: EnumTrait, -) : ServerEnumGenerator( - codegenContext, - writer, - shape, - enumTrait, -) { +) : ServerEnumGenerator(codegenContext, writer, shape) { private val pyo3Symbols = listOf(PythonServerCargoDependency.PyO3.asType()) @@ -49,11 +42,6 @@ class PythonServerEnumGenerator( Attribute.Custom("pyo3::pyclass", symbols = pyo3Symbols).render(writer) } - override fun renderFromForStr() { - renderPyClass() - super.renderFromForStr() - } - private fun renderPyO3Methods() { Attribute.Custom("pyo3::pymethods", symbols = pyo3Symbols).render(writer) writer.rustTemplate( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 91eaf24f9d..61e8b537f4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -67,13 +67,7 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) // TODO Docs - .let { - if (publicConstrainedTypes) ConstrainedShapeSymbolProvider( - it, - model, - serviceShape, - ) else it - } + .let { if (publicConstrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 2b93667475..ef7975218b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter @@ -60,7 +61,6 @@ import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormaliz import software.amazon.smithy.rust.codegen.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.smithy.transformers.RemoveEventStreamOperations import software.amazon.smithy.rust.codegen.util.CommandFailed -import software.amazon.smithy.rust.codegen.util.getTrait import software.amazon.smithy.rust.codegen.util.hasTrait import software.amazon.smithy.rust.codegen.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.util.runCommand @@ -76,7 +76,7 @@ open class ServerCodegenVisitor( ) : ShapeVisitor.Default() { protected val logger = Logger.getLogger(javaClass.name) - protected val settings = ServerRustSettings.from(context.model, context.settings) + protected var settings = ServerRustSettings.from(context.model, context.settings) protected var rustCrate: RustCrate private val fileManifest = context.fileManifest @@ -328,24 +328,33 @@ open class ServerCodegenVisitor( * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. */ override fun stringShape(shape: StringShape) { - shape.getTrait()?.also { enum -> + fun serverEnumGeneratorFactory(codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) = + ServerEnumGenerator(codegenContext, writer, shape) + stringShape(shape, ::serverEnumGeneratorFactory) + } + + protected fun stringShape( + shape: StringShape, + enumShapeGeneratorFactory: (codegenContext: ServerCodegenContext, writer: RustWriter, shape: StringShape) -> ServerEnumGenerator, + ) { + if (shape.hasTrait()) { logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> - ServerEnumGenerator(codegenContext, writer, shape, enum).render() + enumShapeGeneratorFactory(codegenContext, writer, shape).render() ConstrainedTraitForEnumGenerator(model, codegenContext.symbolProvider, writer, shape).render() } } - if (shape.hasTrait() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) { + if (shape.hasTrait() && shape.hasTrait()) { logger.warning( """ - String shape $shape has an `enum` trait and another constraint trait. This is valid according to the Smithy - spec v1 IDL, but it's unclear what the semantics are. In any case, the Smithy CLI should enforce the + String shape $shape has an `enum` trait and the `length` trait. This is valid according to the Smithy + IDL v1 spec, but it's unclear what the semantics are. In any case, the Smithy core libraries should enforce the constraints (which it currently does not), not each code generator. See https://github.com/awslabs/smithy/issues/1121f for more information. """.trimIndent(), ) - } else if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { + } else if (!shape.hasTrait() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") rustCrate.withModule(ModelsModule) { writer -> ConstrainedStringGenerator(codegenContext, writer, shape).render() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index b597f29e4a..c90002a91e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock @@ -16,13 +15,13 @@ import software.amazon.smithy.rust.codegen.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.util.dq +import software.amazon.smithy.rust.codegen.util.expectTrait open class ServerEnumGenerator( val codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: StringShape, - enumTrait: EnumTrait, -) : EnumGenerator(codegenContext.model, codegenContext.symbolProvider, writer, shape, enumTrait) { +) : EnumGenerator(codegenContext.model, codegenContext.symbolProvider, writer, shape, shape.expectTrait()) { override var target: CodegenTarget = CodegenTarget.SERVER private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index 8048bf6a44..3373b09ca8 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -12,7 +12,6 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rust.codegen.util.lookup class ServerEnumGeneratorTest { @@ -41,7 +40,7 @@ class ServerEnumGeneratorTest { @Test fun `it generates TryFrom, FromStr and errors for enums`() { - ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() + ServerEnumGenerator(codegenContext, writer, shape).render() writer.compileAndTest( """ use std::str::FromStr; @@ -54,7 +53,7 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without the unknown variant`() { - ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() + ServerEnumGenerator(codegenContext, writer, shape).render() writer.compileAndTest( """ // check no unknown @@ -69,7 +68,7 @@ class ServerEnumGeneratorTest { @Test fun `it generates enums without non_exhaustive`() { - ServerEnumGenerator(codegenContext, writer, shape, shape.expectTrait()).render() + ServerEnumGenerator(codegenContext, writer, shape).render() writer.toString() shouldNotContain "#[non_exhaustive]" } } From 6d09543e056eae5ccbc1d080299769236df27892 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 14 Sep 2022 14:32:02 +0200 Subject: [PATCH 181/255] ./gradlew ktlintFormat --- .../codegen/server/python/smithy/PythonServerCodegenVisitor.kt | 2 +- .../smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt | 2 +- .../rust/codegen/server/smithy/testutil/ServerTestHelpers.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index df0ea9e2e7..e2240e78ff 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -69,7 +69,7 @@ class PythonServerCodegenVisitor( model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - publicConstrainedTypes: Boolean + publicConstrainedTypes: Boolean, ) = PythonCodegenServerPlugin.baseSymbolProvider(model, serviceShape, symbolVisitorConfig, publicConstrainedTypes) val serverSymbolProviders = ServerSymbolProviders.from( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index ef7975218b..5ab9e6ef93 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -117,7 +117,7 @@ open class ServerCodegenVisitor( service, symbolVisitorConfig, settings.codegenConfig.publicConstrainedTypes, - RustCodegenServerPlugin::baseSymbolProvider + RustCodegenServerPlugin::baseSymbolProvider, ) codegenContext = ServerCodegenContext( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 68e6c2e5d7..3c2c57b613 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -90,7 +90,7 @@ fun serverTestCodegenContext( service, ServerTestSymbolVisitorConfig, settings.codegenConfig.publicConstrainedTypes, - RustCodegenServerPlugin::baseSymbolProvider + RustCodegenServerPlugin::baseSymbolProvider, ) return ServerCodegenContext( From fd0d94ae9f88d97152c418525b4571d8036073d7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 15 Sep 2022 18:03:58 +0200 Subject: [PATCH 182/255] Fix some copyright headers: no full stop at the end of Apache-2.0 --- .../client/smithy/ConstraintViolationSymbolProvider.kt | 2 +- .../amazon/smithy/rust/codegen/client/smithy/Constraints.kt | 5 +++++ .../client/smithy/PubCrateConstrainedShapeSymbolProvider.kt | 2 +- .../client/smithy/UnconstrainedShapeSymbolProvider.kt | 2 +- .../codegen/server/smithy/ConstrainedShapeSymbolProvider.kt | 2 +- .../smithy/PubCrateConstraintViolationSymbolProvider.kt | 2 +- .../server/smithy/generators/ConstrainedMapGenerator.kt | 2 +- .../smithy/generators/ConstrainedMapGeneratorCommon.kt | 2 +- .../smithy/generators/ConstrainedShapeGeneratorCommon.kt | 2 +- .../server/smithy/generators/ConstrainedStringGenerator.kt | 2 +- .../smithy/generators/ConstrainedTraitForEnumGenerator.kt | 2 +- .../smithy/generators/MapConstraintViolationGenerator.kt | 2 +- .../generators/PubCrateConstrainedCollectionGenerator.kt | 2 +- .../smithy/generators/PubCrateConstrainedMapGenerator.kt | 2 +- .../server/smithy/generators/ServerBuilderGenerator.kt | 2 +- .../ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt | 2 +- .../smithy/generators/ServerStructureConstrainedTraitImpl.kt | 2 +- .../smithy/generators/UnconstrainedCollectionGenerator.kt | 2 +- .../server/smithy/generators/UnconstrainedMapGenerator.kt | 2 +- .../server/smithy/generators/UnconstrainedUnionGenerator.kt | 2 +- .../server/smithy/ConstrainedShapeSymbolProviderTest.kt | 2 +- .../smithy/rust/codegen/server/smithy/ConstraintsTest.kt | 2 +- .../smithy/PubCrateConstrainedShapeSymbolProviderTest.kt | 2 +- .../server/smithy/UnconstrainedShapeSymbolProviderTest.kt | 2 +- .../server/smithy/generators/ConstrainedMapGeneratorTest.kt | 2 +- .../smithy/generators/ConstrainedStringGeneratorTest.kt | 2 +- .../generators/UnconstrainedCollectionGeneratorTest.kt | 2 +- .../smithy/generators/UnconstrainedMapGeneratorTest.kt | 2 +- .../smithy/generators/UnconstrainedUnionGeneratorTest.kt | 2 +- rust-runtime/inlineable/src/constrained.rs | 2 +- 30 files changed, 34 insertions(+), 29 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt index 9219f11409..240cce3b95 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.client.smithy diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt index f04e7fd1ad..6de30aea91 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.client.smithy import software.amazon.smithy.codegen.core.SymbolProvider diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 194eccaac5..f7f84f1ac3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.client.smithy diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt index ca98e3346d..bb6ef70574 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.client.smithy diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 4eb8241d76..5562c6d6f5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt index 54c168b229..2ce6236214 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 1bf9fa5433..fb56539119 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt index 9655b0b79c..2832290011 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt index d372a33582..ddf9c81116 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 8acb6b24d3..fc4eb67ea8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 5c89734a5c..6b04f73940 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 4eb8682815..6be27457a6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index da0a9a4e53..74a1581e4c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 7c883db94f..fbdedac9be 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index fcf81d7c1d..c0f77eab4b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 082b7a669b..31ef80d909 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt index 7c15921d8e..4341f0b791 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index b15ea993f8..5de627a2fc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 72a354ed63..2ee79f5697 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index d940719f7a..d4ccc5a723 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt index 84b650654e..eec6073504 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index 6968a75446..d3bad172b5 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt index f70823855a..cb1f9be8c0 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index dd36941ad7..a87ae391e2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index ac3dd5b398..cffe575dac 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 9050b8cf71..7d6b9a055a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 884fcdfa5b..98aed57b91 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index e2f0a319b1..2d79b929f5 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index cdae0ed246..0577b5babc 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators diff --git a/rust-runtime/inlineable/src/constrained.rs b/rust-runtime/inlineable/src/constrained.rs index 7ce2db1059..1276eccbcd 100644 --- a/rust-runtime/inlineable/src/constrained.rs +++ b/rust-runtime/inlineable/src/constrained.rs @@ -1,6 +1,6 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. + * SPDX-License-Identifier: Apache-2.0 */ pub(crate) trait Constrained { From f8c700c0ddbb2d74be5c6e2fa6a7dc02731c05b9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 16 Sep 2022 15:15:04 +0200 Subject: [PATCH 183/255] Refactor Constraints.kt --- .../rust/codegen/client/smithy/Constraints.kt | 24 ++++++++----------- .../PubCrateConstrainedCollectionGenerator.kt | 4 ++-- .../PubCrateConstrainedMapGenerator.kt | 6 ++--- .../codegen/server/smithy/ConstraintsTest.kt | 17 ------------- 4 files changed, 15 insertions(+), 36 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt index 6de30aea91..0d23ef7ef1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt @@ -18,13 +18,15 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.model.traits.PatternTrait -import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.rust.codegen.client.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.hasTrait -// TODO Move this file to `core` or `server`. +/** + * This file contains utilities to work with constrained shapes. + * + * TODO Move this file to `core` or `server`. + */ /** * We say a shape is _directly_ constrained if: @@ -68,6 +70,9 @@ fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled( /** * Helper function to determine whether a shape will map to a _public_ constrained wrapper tuple type. + * + * This function is used in core code generators, so it takes in a [CoreCodegenContext] that is downcast + * to [ServerCodegenContext] when generating servers. */ fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, coreCodegenContext: CoreCodegenContext) = coreCodegenContext.target == CodegenTarget.SERVER && @@ -87,13 +92,13 @@ fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, coreCodegenContex * Note how we short-circuit on `publicConstrainedTypes = true`, but we still require it to be passed in instead of laying * the responsibility on the caller, for API safety usage. */ -fun Shape.containsNonPublicType( +fun Shape.typeNameContainsNonPublicType( model: Model, symbolProvider: SymbolProvider, publicConstrainedTypes: Boolean, ): Boolean = !publicConstrainedTypes && when (this) { is SimpleShape -> wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model) - is MemberShape -> model.expectShape(this.target).containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + is MemberShape -> model.expectShape(this.target).typeNameContainsNonPublicType(model, symbolProvider, publicConstrainedTypes) is CollectionShape -> this.canReachConstrainedShape(model, symbolProvider) is MapShape -> this.canReachConstrainedShape(model, symbolProvider) is StructureShape, is UnionShape -> false @@ -103,15 +108,6 @@ fun Shape.containsNonPublicType( fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) -// TODO seems unused -fun MemberShape.requiresNewtype() = - // Note that member shapes whose only constraint trait is `required` do not require a newtype. - this.hasTrait() || - this.hasTrait() || - // `uniqueItems` is deprecated, so we ignore it. - // this.hasTrait() || - this.hasTrait() - fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider) = this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 74a1581e4c..2701460153 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.containsNonPublicType +import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained @@ -125,7 +125,7 @@ class PubCrateConstrainedCollectionGenerator( *codegenScope, ) } else { - val innerNeedsConversion = innerShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + val innerNeedsConversion = innerShape.typeNameContainsNonPublicType(model, symbolProvider, publicConstrainedTypes) rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index fbdedac9be..c1f0c35405 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.containsNonPublicType +import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained @@ -116,8 +116,8 @@ class PubCrateConstrainedMapGenerator( *codegenScope, ) } else { - val keyNeedsConversion = keyShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) - val valueNeedsConversion = valueShape.containsNonPublicType(model, symbolProvider, publicConstrainedTypes) + val keyNeedsConversion = keyShape.typeNameContainsNonPublicType(model, symbolProvider, publicConstrainedTypes) + val valueNeedsConversion = valueShape.typeNameContainsNonPublicType(model, symbolProvider, publicConstrainedTypes) rustTemplate( """ diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index d3bad172b5..ac535855d4 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.requiresNewtype import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider @@ -135,20 +134,4 @@ class ConstraintsTest { mapB.canReachConstrainedShape(model, symbolProvider) shouldBe true recursiveShape.canReachConstrainedShape(model, symbolProvider) shouldBe true } - - @Test - fun `only some constraint traits on member shapes should warrant a newtype`() { - structAInt.requiresNewtype() shouldBe true - structAString.requiresNewtype() shouldBe false - - val structBPatternString = model.lookup("test#StructureB\$patternString") - val structBRequiredString = model.lookup("test#StructureB\$requiredString") - val structBMapA = model.lookup("test#StructureB\$mapA") - val structBMapAPrecedence = model.lookup("test#StructureB\$mapAPrecedence") - - structBPatternString.requiresNewtype() shouldBe true - structBRequiredString.requiresNewtype() shouldBe false - structBMapA.requiresNewtype() shouldBe false - structBMapAPrecedence.requiresNewtype() shouldBe true - } } From c491fb35ff756f853f279ee0cbf0b51fac801540 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 16 Sep 2022 16:53:15 +0200 Subject: [PATCH 184/255] Inline deserializerBuilderSetterName, adjust TODOs --- .../smithy/generators/BuilderGenerator.kt | 3 ++- .../smithy/generators/StructureGenerator.kt | 20 +---------------- .../parse/EventStreamUnmarshallerGenerator.kt | 6 ++--- .../protocols/parse/JsonParserGenerator.kt | 8 +++---- .../ServerHttpBoundProtocolGenerator.kt | 22 +++++++++---------- 5 files changed, 21 insertions(+), 38 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/BuilderGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/BuilderGenerator.kt index f3002c9092..1400513732 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/BuilderGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/BuilderGenerator.kt @@ -35,6 +35,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +// TODO Move this to `core`. fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) @@ -47,7 +48,7 @@ fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { .build() } -// TODO Place in a server file. +// TODO Move this to `core`. fun StructureShape.serverBuilderSymbol(symbolProvider: RustSymbolProvider, pubCrate: Boolean): Symbol { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt index c0a52cc8f6..b0f00431bf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt @@ -39,7 +39,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrai import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, block: RustWriter.() -> Unit) { rustBlock("impl ${symbolProvider.toSymbol(structureShape).name}") { @@ -55,24 +54,7 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } } -/** - * The name of the builder's setter the deserializer should use. - * Setter names will never hit a reserved word and therefore never need escaping. - */ -fun MemberShape.deserializerBuilderSetterName(codegenTarget: CodegenTarget) = - when (codegenTarget) { - // TODO Both these arms return the same result. - CodegenTarget.CLIENT -> this.setterName() - CodegenTarget.SERVER -> "set_${this.memberName.toSnakeCase()}" - } - -// TODO Cleanup (old implementation of above extension function). -// return if (this.targetCanReachConstrainedShape(model, symbolProvider)) { -// "set_${this.memberName.toSnakeCase()}" -// } else { -// this.memberName.toSnakeCase() -// } - +// TODO Move this to `core`. fun StructureShape.builderSymbol( coreCodegenContext: CoreCodegenContext, symbolProvider: RustSymbolProvider, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 5934790b8d..65042bcaaf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -36,9 +36,9 @@ import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.client.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.client.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.client.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.client.smithy.generators.renderUnknownVariant +import software.amazon.smithy.rust.codegen.client.smithy.generators.setterName import software.amazon.smithy.rust.codegen.client.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.client.smithy.transformers.eventStreamErrors import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticEventStreamUnionTrait @@ -227,7 +227,7 @@ class EventStreamUnmarshallerGenerator( } private fun RustWriter.renderUnmarshallEventHeader(member: MemberShape) { - withBlock("builder = builder.${member.deserializerBuilderSetterName(codegenTarget)}(", ");") { + withBlock("builder = builder.${member.setterName()}(", ");") { conditionalBlock("Some(", ")", member.isOptional) { when (val target = model.expectShape(member.target)) { is BooleanShape -> rustTemplate("#{expect_fns}::expect_bool(header)?", *codegenScope) @@ -262,7 +262,7 @@ class EventStreamUnmarshallerGenerator( *codegenScope, ) } - withBlock("builder = builder.${member.deserializerBuilderSetterName(codegenTarget)}(", ");") { + withBlock("builder = builder.${member.setterName()}(", ");") { conditionalBlock("Some(", ")", member.isOptional) { when (target) { is BlobShape -> { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt index ad873841b5..055de0a838 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt @@ -44,8 +44,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.CodegenTarge import software.amazon.smithy.rust.codegen.client.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.client.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.client.smithy.generators.renderUnknownVariant +import software.amazon.smithy.rust.codegen.client.smithy.generators.setterName import software.amazon.smithy.rust.codegen.client.smithy.isOptional import software.amazon.smithy.rust.codegen.client.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBindingResolver @@ -220,7 +220,7 @@ class JsonParserGenerator( CodegenTarget.CLIENT -> { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) + member.setterName() }(", ");", ) { @@ -231,7 +231,7 @@ class JsonParserGenerator( if (symbolProvider.toSymbol(member).isOptional()) { withBlock( "builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) + member.setterName() }(", ");", ) { @@ -244,7 +244,7 @@ class JsonParserGenerator( """ { builder = builder.${ - member.deserializerBuilderSetterName(codegenTarget) + member.setterName() }(v); } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 3f71aafaf0..73d6db4524 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -48,13 +48,13 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.OperationCust import software.amazon.smithy.rust.codegen.client.smithy.generators.CodegenTarget import software.amazon.smithy.rust.codegen.client.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.TypeConversionGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.deserializerBuilderSetterName import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.client.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.client.smithy.generators.setterName import software.amazon.smithy.rust.codegen.client.smithy.isOptional import software.amazon.smithy.rust.codegen.client.smithy.mapRustType import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBindingDescriptor @@ -777,12 +777,12 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rust( """ { - input = input.${member.deserializerBuilderSetterName(codegenContext.target)}(${ - if (symbolProvider.toSymbol(binding.member).isOptional()) { - "Some(value)" - } else { - "value" - } + input = input.${member.setterName()}(${ + if (symbolProvider.toSymbol(binding.member).isOptional()) { + "Some(value)" + } else { + "value" + } }); } """, @@ -939,7 +939,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val deserializer = generateParseStrFn(binding, true) rustTemplate( """ - input = input.${binding.member.deserializerBuilderSetterName(codegenContext.target)}( + input = input.${binding.member.setterName()}( #{deserializer}(m$index)? ); """, @@ -1032,7 +1032,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !seen_$memberName && k == "${it.locationName}" { - input = input.${it.member.deserializerBuilderSetterName(codegenContext.target)}( + input = input.${it.member.setterName()}( #{deserializer}(&v)? ); seen_$memberName = true; @@ -1123,7 +1123,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } if (queryParamsBinding != null) { val isOptional = unconstrainedShapeSymbolProvider.toSymbol(queryParamsBinding.member).isOptional() - withBlock("input = input.${queryParamsBinding.member.deserializerBuilderSetterName(codegenContext.target)}(", ");") { + withBlock("input = input.${queryParamsBinding.member.setterName()}(", ");") { conditionalBlock("Some(", ")", conditional = isOptional) { write("query_params") } @@ -1139,7 +1139,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustBlock("if !$memberName.is_empty()") { withBlock( "input = input.${ - binding.member.deserializerBuilderSetterName(codegenContext.target) + binding.member.setterName() }(", ");", ) { From 97f5955f72fad18d712c1609765c9e3be70c3a00 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 16 Sep 2022 18:23:29 +0200 Subject: [PATCH 185/255] Docs; simplified renderBuilderMemberFn --- .../UnconstrainedShapeSymbolProvider.kt | 4 +- .../smithy/generators/StructureGenerator.kt | 7 +- ...ShapesReachableFromOperationInputTagger.kt | 23 ++++- .../rust/codegen/client/testutil/Rust.kt | 1 - .../smithy/rust/codegen/core/util/Smithy.kt | 2 +- .../smithy/PythonCodegenServerPlugin.kt | 8 +- .../server/smithy/RustCodegenServerPlugin.kt | 6 +- .../server/smithy/ServerSymbolProviders.kt | 1 - .../ConstrainedTraitForEnumGenerator.kt | 3 +- .../generators/ServerBuilderGenerator.kt | 99 ++++++++++--------- ...rGeneratorWithoutPublicConstrainedTypes.kt | 4 +- 11 files changed, 91 insertions(+), 67 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt index bb6ef70574..9c28eef163 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt @@ -22,8 +22,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilde import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO Move this to `core` or `server`. - /** * The [UnconstrainedShapeSymbolProvider] returns, _for a given constrained * shape_, a symbol whose Rust type can hold the corresponding unconstrained @@ -65,6 +63,8 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * `codegen-server` subproject), because that symbol provider will return a * constrained type for shapes that have constraint traits attached. * + * TODO Move this to `core`; remove below sentence. + * * While this symbol provider is only used by the server, it needs to be in the * `codegen` subproject because the (common to client and server) parsers use * it. diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt index b0f00431bf..6c1b7ee10f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt @@ -109,7 +109,12 @@ open class StructureGenerator( * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a * fallible builder to be constructed. */ - fun serverHasFallibleBuilder(structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider, takeInUnconstrainedTypes: Boolean) = + fun serverHasFallibleBuilder( + structureShape: StructureShape, + model: Model, + symbolProvider: SymbolProvider, + takeInUnconstrainedTypes: Boolean, + ) = if (takeInUnconstrainedTypes) { structureShape.canReachConstrainedShape(model, symbolProvider) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt index 94716391d0..db9489ef30 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer @@ -17,8 +16,23 @@ import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticAggregate import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE /** - * TODO Docs - * TODO Move this to server or core. + * Tag all [aggregate shapes] reachable from operation input with the + * [SyntheticAggregateShapeReachableFromOperationInputTagTrait] tag. + * + * This is useful to determine whether we need to generate code to + * enforce constraints upon request deserialization in the server. + * + * This needs to be a model transformer; it cannot be lazily calculated + * when needed. This is because other model transformers may transform + * the model such that aggregate shapes that were reachable from operation + * input are no longer so. For example, [EventStreamNormalizer] pulls + * event stream error variants out of the union shape where they are defined. + * As such, [AggregateShapesReachableFromOperationInputTagger] needs to run + * before these model transformers. + * + * [aggregate shapes]: https://awslabs.github.io/smithy/2.0/spec/aggregate-types.html#aggregate-types + * + * TODO Move this to `core`, together with all the model transformers. */ object AggregateShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { @@ -32,13 +46,12 @@ object AggregateShapesReachableFromOperationInputTagger { return ModelTransformer.create().mapShapes(model) { shape -> when (shape) { - is StructureShape, is UnionShape, is ListShape, is SetShape, is MapShape -> { + is StructureShape, is UnionShape, is ListShape, is MapShape -> { if (shapesReachableFromOperationInputs.contains(shape)) { val builder = when (shape) { is StructureShape -> shape.toBuilder() is UnionShape -> shape.toBuilder() is ListShape -> shape.toBuilder() - is SetShape -> shape.toBuilder() is MapShape -> shape.toBuilder() else -> UNREACHABLE("the `when` is exhaustive") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/Rust.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/Rust.kt index ea2bb0298f..a3e21dba78 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/Rust.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/Rust.kt @@ -90,7 +90,6 @@ object TestWorkspace { } } - // TODO This should not be used by the server because it's using ClientCodegenConfig. @Suppress("NAME_SHADOWING") fun testProject(symbolProvider: RustSymbolProvider? = null, debugMode: Boolean = false): TestWriterDelegator { val subprojectDir = subproject() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index dc678647af..5a0b94e75a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -71,7 +71,7 @@ fun Shape.hasEventStreamMember(model: Model): Boolean { } private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { - is StructureShape, is UnionShape, is ListShape, is SetShape, is MapShape -> { + is StructureShape, is UnionShape, is ListShape, is MapShape -> { shape.hasTrait() } else -> PANIC("this method does not support shape type ${shape.type}") } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt index 8944999bc3..54a0d31612 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt @@ -67,13 +67,15 @@ class PythonCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - publicConstrainedTypes: Boolean = true, + constrainedTypes: Boolean = true, ) = // Rename a set of symbols that do not implement `PyClass` and have been wrapped in // `aws_smithy_http_server_python::types`. PythonServerSymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) - // TODO Docs - .let { if (publicConstrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + // Generate public constrained types for directly constrained shapes. + // In the Python server project, this is only done to generate constrained types for simple shapes (e.g. + // a `string` shape with the `length` trait), but these always remain `pub(crate)`. + .let { if (constrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt index 39236341fc..d78519aa06 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCodegenServerPlugin.kt @@ -63,11 +63,11 @@ class RustCodegenServerPlugin : SmithyBuildPlugin { model: Model, serviceShape: ServiceShape, symbolVisitorConfig: SymbolVisitorConfig, - publicConstrainedTypes: Boolean = true, + constrainedTypes: Boolean = true, ) = SymbolVisitor(model, serviceShape = serviceShape, config = symbolVisitorConfig) - // TODO Docs - .let { if (publicConstrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } + // Generate public constrained types for directly constrained shapes. + .let { if (constrainedTypes) ConstrainedShapeSymbolProvider(it, model, serviceShape) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) .let { EventStreamSymbolProvider(symbolVisitorConfig.runtimeConfig, it, model, CodegenTarget.SERVER) } // Generate [ByteStream] instead of `Blob` for streaming binary shapes (e.g. S3 GetObject) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt index 66028225ce..f07535030f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt @@ -42,7 +42,6 @@ class ServerSymbolProviders private constructor( symbolVisitorConfig, true, ), - // TODO Shouldn't `UnconstrainedShapeSymbolProvider` be applied at the same level as the `ConstrainedShapeSymbolProvider`? unconstrainedShapeSymbolProvider = UnconstrainedShapeSymbolProvider( baseSymbolProviderFactory( model, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 6b04f73940..ad5a8dda74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -16,7 +16,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait /** - * [ConstrainedTraitForEnumGenerator] TODO Docs + * [ConstrainedTraitForEnumGenerator] generates code that implements the [RuntimeType.ConstrainedTrait] trait on an + * enum shape. */ class ConstrainedTraitForEnumGenerator( val model: Model, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index c0f77eab4b..f35d72c21b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,10 +9,8 @@ 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.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.client.rustlang.Attribute import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.client.rustlang.RustType @@ -36,7 +34,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.client.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilderSymbol @@ -59,13 +56,39 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType -// TODO Document differences: -// - This one takes in codegenContext. -// - Unlike in `BuilderGenerator.kt`, we don't add helper methods to add items to vectors and hash maps. -// - This builder is not `PartialEq`. -// - Always implements either From for Structure or TryFrom for Structure. -// - `pubCrateConstrainedShapeSymbolProvider` only needed if we want the builder to take in unconstrained types. -// - This builder is `pub(crate)` if `publicConstrainedTypes` is false. +/** + * Generates a builder for the Rust type associated with the [StructureShape]. + * + * This generator is meant for use by the server project. Clients use the [BuilderGenerator] from the `codegen-client` + * Gradle subproject instead. + * + * This builder is different in that it enforces [constraint traits] upon calling `.build()`. If any constraint + * violations occur, the `build` method returns them. + * + * These are the main differences with the builders generated by the client's [BuilderGenerator]: + * + * - The design of this builder is simpler and closely follows what you get when using the [derive_builder] crate: + * * The builder may have one method per struct member named _exactly_ like the struct member and whose input type + * matches _exactly_ the struct's member type. This method is generated by [renderBuilderMemberFn]. + * * The builder may have one _setter_ method (i.e. prefixed with `set_`) per struct member whose input type is the + * corresponding _unconstrained type_ for the member. This method is always `pub(crate)` and meant for use for + * server deserializers only. + * * There are no convenience methods to add items to vector and hash map struct members. + * - The builder is not `PartialEq`. This is because the builder's members may or may not have been constrained (their + * types hold `MaybeConstrained`, and so it doesn't make sense to compare e.g. two builders holding the same data + * values, but one builder holds the member in the constrained variant while the other one holds it in the unconstrained + * variant. + * - The builder always implement `TryFrom for Structure` or `From for Structure`, depending on whether + * the structure is constrained (and hence enforcing the constraints might yield an error) or not, respectively. + * + * The builder is `pub(crate)` when `publicConstrainedTypes` is `false`, since in this case the user is never exposed + * to constrained types, and only the server's deserializers need to enforce constraint traits upon receiving a request. + * The user is exposed to [ServerBuilderGeneratorWithoutPublicConstrainedTypes] in this case instead, which intentionally + * _does not_ enforce constraints. + * + * [constraint traits]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html + * [derive_builder]: https://docs.rs/derive_builder/latest/derive_builder/index.html + */ class ServerBuilderGenerator( codegenContext: ServerCodegenContext, private val shape: StructureShape, @@ -149,8 +172,8 @@ class ServerBuilderGenerator( } writer.docs("A builder for #D.", structureSymbol) - // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. - // TODO Manually implement `Default` so that we can add custom docs. + // Matching derives to the main structure, - `PartialEq` (see class documentation for why), + `Default` + // since we are a builder and everything is optional. val baseDerives = structureSymbol.expectRustMetadata().derives val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) @@ -172,7 +195,7 @@ class ServerBuilderGenerator( } } - // TODO This impl does not take into account sensitive trait. + // TODO This impl does not take into account the `sensitive` trait. private fun renderImplDisplayConstraintViolation(writer: RustWriter) { writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { @@ -228,10 +251,9 @@ class ServerBuilderGenerator( private fun renderBuildFn(implBlockWriter: RustWriter) { implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) if (isBuilderFallible) { - // TODO Docs not accurate. Structure may not have any optional members. implBlockWriter.docs( """ - The builder fails to construct a #D if you do not provide a value for all non-`Option`al members. + The builder fails to construct a #D if a [`ConstraintViolation`] occurs. """, structureSymbol, ) @@ -240,10 +262,14 @@ class ServerBuilderGenerator( implBlockWriter.docs("If the builder fails, it will return the _first_ encountered [`ConstraintViolation`].") } } - // TODO Could be single block - implBlockWriter.rustBlockTemplate("pub fn build(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { - rust("self.build_enforcing_all_constraints()") - } + implBlockWriter.rustTemplate( + """ + pub fn build(self) -> #{ReturnType:W} { + self.build_enforcing_all_constraints() + } + """, + "ReturnType" to buildFnReturnType(), + ) renderBuildEnforcingAllConstraintsFn(implBlockWriter) } @@ -283,6 +309,7 @@ class ServerBuilderGenerator( writer: RustWriter, member: MemberShape, ) { + check(publicConstrainedTypes) val symbol = symbolProvider.toSymbol(member) val memberName = symbolProvider.toMemberName(member) @@ -304,28 +331,8 @@ class ServerBuilderGenerator( writer.rustBlock("pub fn $memberName(mut self, input: ${symbol.rustType().render()}) -> Self") { withBlock("self.$memberName = ", "; self") { conditionalBlock("Some(", ")", conditional = !symbol.isOptional()) { - val targetShape = model.expectShape(member.target) - // If constrained types are not public and the target shape is one that would generate a public constrained - // type had the `publicConstrainedTypes` setting been enabled, then we need to: - // 1. constrain the input type into the corresponding `pub(crate)` unconstrained types first; and - // 2. store the resulting value in a `MaybeConstrained::Unconstrained` variant. - // This condition is calculated once here and used later when the above two steps are performed. - // Note we explicitly opt out when the target shape is a structure shape or a string shape with the - // `enum` trait, since in those cases public constrained types are _always_ generated, regardless of - // whether `publicConstrainedTypes` is enabled (remember structure shapes are directly constrained - // when at least one of their members is non-optional). - // TODO I should be able to remove this whole thing because now this method is only called when `publicConstrainedTypes` is `true`. - val isInputFullyUnconstrained = !publicConstrainedTypes && - targetShape.canReachConstrainedShape(model, symbolProvider) && - !(targetShape is StringShape && targetShape.hasTrait()) && - targetShape !is StructureShape - - val maybeConstrainedVariant = if (isInputFullyUnconstrained) { - // Step 2 above. - "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Unconstrained" - } else { + val maybeConstrainedVariant = "${symbol.makeMaybeConstrained().rustType().namespace}::MaybeConstrained::Constrained" - } var varExpr = if (symbol.isOptional()) "v" else "input" if (hasBox) varExpr = "*$varExpr" @@ -409,7 +416,7 @@ class ServerBuilderGenerator( private fun constraintViolations() = members.flatMap { member -> listOfNotNull( - builderMissingFieldForMember(member, symbolProvider), + builderMissingFieldConstraintViolationForMember(member, symbolProvider), builderConstraintViolationForMember(member), ) } @@ -557,7 +564,7 @@ class ServerBuilderGenerator( } } } - builderMissingFieldForMember(member, symbolProvider)?.also { + builderMissingFieldConstraintViolationForMember(member, symbolProvider)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") } } @@ -580,7 +587,7 @@ enum class ConstraintViolationKind { data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { fun name() = when (kind) { ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "${forMember.memberName.toPascalCase()}" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> forMember.memberName.toPascalCase() } /** @@ -605,13 +612,11 @@ fun constraintViolationMessage( } } -// TODO Extract to common /** * Returns the builder failure associated with the [member] field if it is `required`. */ -fun builderMissingFieldForMember(member: MemberShape, symbolProvider: RustSymbolProvider) = -// TODO(https://github.com/awslabs/smithy-rs/issues/1302): We go through the symbol provider because - // non-`required` blob streaming members are interpreted as `required`, so we can't use `member.isOptional`. +fun builderMissingFieldConstraintViolationForMember(member: MemberShape, symbolProvider: RustSymbolProvider) = + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. if (symbolProvider.toSymbol(member).isOptional()) { null } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 31ef80d909..453070a780 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -171,7 +171,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( val memberName = symbolProvider.toMemberName(member) withBlock("$memberName: self.$memberName", ",") { - builderMissingFieldForMember(member, symbolProvider)?.also { + builderMissingFieldConstraintViolationForMember(member, symbolProvider)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") } } @@ -217,7 +217,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } private fun constraintViolations() = members.flatMap { member -> - listOfNotNull(builderMissingFieldForMember(member, symbolProvider)) + listOfNotNull(builderMissingFieldConstraintViolationForMember(member, symbolProvider)) } private fun renderTryFromBuilderImpl(writer: RustWriter) { From c6ac7594c2218def54376838d40ee8d5db5c8bb8 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 16 Sep 2022 20:31:44 +0200 Subject: [PATCH 186/255] ./gradlew ktlintFormat --- .../protocols/parse/JsonParserGenerator.kt | 18 +++--------------- .../smithy/rust/codegen/core/util/Smithy.kt | 1 - .../PubCrateConstrainedCollectionGenerator.kt | 2 +- .../PubCrateConstrainedMapGenerator.kt | 2 +- .../ServerHttpBoundProtocolGenerator.kt | 12 ++++++------ 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt index 055de0a838..cd3c62ea25 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt @@ -218,23 +218,13 @@ class JsonParserGenerator( rustBlock("${jsonName(member).dq()} =>") { when (codegenTarget) { CodegenTarget.CLIENT -> { - withBlock( - "builder = builder.${ - member.setterName() - }(", - ");", - ) { + withBlock("builder = builder.${member.setterName()}(", ");") { deserializeMember(member) } } CodegenTarget.SERVER -> { if (symbolProvider.toSymbol(member).isOptional()) { - withBlock( - "builder = builder.${ - member.setterName() - }(", - ");", - ) { + withBlock("builder = builder.${member.setterName()}(", ");") { deserializeMember(member) } } else { @@ -243,9 +233,7 @@ class JsonParserGenerator( rust( """ { - builder = builder.${ - member.setterName() - }(v); + builder = builder.${member.setterName()}(v); } """, ) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 5a0b94e75a..2482431f4e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.NumberShape 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.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 2701460153..2487c3e46d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -14,9 +14,9 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType /** * A generator for a wrapper tuple newtype over a collection shape's symbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index c1f0c35405..b84900549f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -15,9 +15,9 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType /** * A generator for a wrapper tuple newtype over a map shape's symbol type. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 73d6db4524..d0eabc7cbd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -778,11 +778,11 @@ private class ServerHttpBoundProtocolTraitImplGenerator( """ { input = input.${member.setterName()}(${ - if (symbolProvider.toSymbol(binding.member).isOptional()) { - "Some(value)" - } else { - "value" - } + if (symbolProvider.toSymbol(binding.member).isOptional()) { + "Some(value)" + } else { + "value" + } }); } """, @@ -1139,7 +1139,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( rustBlock("if !$memberName.is_empty()") { withBlock( "input = input.${ - binding.member.setterName() + binding.member.setterName() }(", ");", ) { From a79db742c7bf77c7fc1cc5805516df96885f58db Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 19 Sep 2022 15:17:12 +0200 Subject: [PATCH 187/255] Make ServerBuilder constraint violation rendering DRYer --- .../protocols/parse/JsonParserGenerator.kt | 3 - .../ServerBuilderConstraintViolations.kt | 167 ++++++++++++++++ .../generators/ServerBuilderGenerator.kt | 184 ++---------------- ...rGeneratorWithoutPublicConstrainedTypes.kt | 101 +++------- 4 files changed, 216 insertions(+), 239 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt index cd3c62ea25..ae11298632 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/parse/JsonParserGenerator.kt @@ -59,9 +59,6 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.utils.StringUtils -// TODO: Separate commit: Make all functions pub(crate). If the functions have in their type signature a pub(crate) type, -// and the function is declared `pub`, Rust will complain, even if the json_deser module is not `pub`. - class JsonParserGenerator( private val coreCodegenContext: CoreCodegenContext, private val httpBindingResolver: HttpBindingResolver, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt new file mode 100644 index 0000000000..28b33bf2cf --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -0,0 +1,167 @@ +package software.amazon.smithy.rust.codegen.server.smithy.generators + +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.StructureShape +import software.amazon.smithy.rust.codegen.client.rustlang.Attribute +import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.client.rustlang.docs +import software.amazon.smithy.rust.codegen.client.rustlang.rust +import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait +import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.isOptional +import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +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.toPascalCase +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider + +/** + * Renders constraint violation types that arise when building a structure shape builder. + * + * Used by [ServerBuilderGenerator] and [ServerBuilderGeneratorWithoutPublicConstrainedTypes]. + */ +class ServerBuilderConstraintViolations( + codegenContext: ServerCodegenContext, + private val shape: StructureShape, + private val builderTakesInUnconstrainedTypes: Boolean, +) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val constraintViolationSymbolProvider = + with(codegenContext.constraintViolationSymbolProvider) { + if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + private val members: List = shape.allMembers.values.toList() + val all = members.flatMap { member -> + listOfNotNull( + forMember(member), + builderConstraintViolationForMember(member), + ) + } + + fun render(writer: RustWriter, nonExhaustive: Boolean) { + Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) + writer.docs("Holds one variant for each of the ways the builder can fail.") + if (nonExhaustive) Attribute.NonExhaustive.render(writer) + val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name + writer.rustBlock("pub enum $constraintViolationSymbolName") { + renderConstraintViolations(writer) + } + renderImplDisplayConstraintViolation(writer) + writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) + } + + /** + * Returns the builder failure associated with the `member` field if its target is constrained. + */ + fun builderConstraintViolationForMember(member: MemberShape) = + if (builderTakesInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { + ConstraintViolation(member, ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) + } else { + null + } + + /** + * Returns the builder failure associated with the [member] field if it is `required`. + */ + fun forMember(member: MemberShape): ConstraintViolation? { + check(members.contains(member)) + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. + return if (symbolProvider.toSymbol(member).isOptional()) { + null + } else { + ConstraintViolation(member, ConstraintViolationKind.MISSING_MEMBER) + } + } + + // TODO This impl does not take into account the `sensitive` trait. + private fun renderImplDisplayConstraintViolation(writer: RustWriter) { + writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { + rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("match self") { + all.forEach { + val arm = if (it.hasInner()) { + "ConstraintViolation::${it.name()}(_)" + } else { + "ConstraintViolation::${it.name()}" + } + rust("""$arm => write!(f, "${it.message(symbolProvider, model)}"),""") + } + } + } + } + } + + private fun renderConstraintViolations(writer: RustWriter) { + for (constraintViolation in all) { + when (constraintViolation.kind) { + ConstraintViolationKind.MISSING_MEMBER -> { + writer.docs("${constraintViolation.message(symbolProvider, model).replaceFirstChar { it.uppercase() }}.") + writer.rust("${constraintViolation.name()},") + } + + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { + val targetShape = model.expectShape(constraintViolation.forMember.target) + + val constraintViolationSymbol = + constraintViolationSymbolProvider.toSymbol(targetShape) + // If the corresponding structure's member is boxed, box this constraint violation symbol too. + .letIf(constraintViolation.forMember.hasTrait()) { + it.makeRustBoxed() + } + + // Note we cannot express the inner constraint violation as `>::Error`, because `T` might + // be `pub(crate)` and that would leak `T` in a public interface. + writer.docs("${constraintViolation.message(symbolProvider, model)}.") + Attribute.DocHidden.render(writer) + writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) + } + } + } + } +} + +/** + * The kinds of constraint violations that can occur when building the builder. + */ +enum class ConstraintViolationKind { + // A field is required but was not provided. + MISSING_MEMBER, + + // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. + CONSTRAINED_SHAPE_FAILURE, +} + +data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { + fun name() = when (kind) { + ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> forMember.memberName.toPascalCase() + } + + /** + * Whether the constraint violation is a Rust tuple struct with one element. + */ + fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE + + /** + * A message for a `ConstraintViolation` variant. This is used in both Rust documentation and the `Display` trait implementation. + */ + fun message(symbolProvider: SymbolProvider, model: Model): String { + val memberName = symbolProvider.toMemberName(forMember) + val structureSymbol = symbolProvider.toSymbol(model.expectShape(forMember.container)) + return when (kind) { + ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" + // TODO Nest errors. + ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index f35d72c21b..e36909754c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -6,8 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators 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.StructureShape import software.amazon.smithy.model.shapes.UnionShape @@ -31,8 +29,6 @@ import software.amazon.smithy.rust.codegen.client.rustlang.withBlock import software.amazon.smithy.rust.codegen.client.rustlang.writable import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.client.smithy.generators.StructureGenerator @@ -51,9 +47,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrai import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType /** @@ -97,21 +91,16 @@ class ServerBuilderGenerator( private val model = codegenContext.model private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val symbolProvider = codegenContext.symbolProvider - private val constraintViolationSymbolProvider = - with(codegenContext.constraintViolationSymbolProvider) { - if (publicConstrainedTypes) { - this - } else { - PubCrateConstraintViolationSymbolProvider(this) - } - } private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) private val moduleName = builderSymbol.namespace.split(builderSymbol.namespaceDelimiter).last() - private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val isBuilderFallible = + StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val serverBuilderConstraintViolations = + ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes) private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), @@ -136,25 +125,7 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) - writer.docs("Holds one variant for each of the ways the builder can fail.") - Attribute.NonExhaustive.render(writer) - val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name - writer.rustBlock("pub enum $constraintViolationSymbolName") { - constraintViolations().forEach { - renderConstraintViolation( - this, - it, - model, - constraintViolationSymbolProvider, - symbolProvider, - structureSymbol, - ) - } - } - - renderImplDisplayConstraintViolation(writer) - writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) + serverBuilderConstraintViolations.render(writer, nonExhaustive = true) // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is // an operation input shape. @@ -195,24 +166,6 @@ class ServerBuilderGenerator( } } - // TODO This impl does not take into account the `sensitive` trait. - private fun renderImplDisplayConstraintViolation(writer: RustWriter) { - writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - rustBlock("match self") { - constraintViolations().forEach { - val arm = if (it.hasInner()) { - "ConstraintViolation::${it.name()}(_)" - } else { - "ConstraintViolation::${it.name()}" - } - rust("""$arm => write!(f, "${constraintViolationMessage(it, symbolProvider, structureSymbol)}"),""") - } - } - } - } - } - private fun renderImplFromConstraintViolationForRequestRejection(writer: RustWriter) { writer.rustTemplate( """ @@ -240,14 +193,6 @@ class ServerBuilderGenerator( ) } - private fun buildFnReturnType() = writable { - if (isBuilderFallible) { - rust("Result<#T, ConstraintViolation>", structureSymbol) - } else { - rust("#T", structureSymbol) - } - } - private fun renderBuildFn(implBlockWriter: RustWriter) { implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) if (isBuilderFallible) { @@ -258,7 +203,7 @@ class ServerBuilderGenerator( structureSymbol, ) - if (constraintViolations().size > 1) { + if (serverBuilderConstraintViolations.all.size > 1) { implBlockWriter.docs("If the builder fails, it will return the _first_ encountered [`ConstraintViolation`].") } } @@ -268,13 +213,16 @@ class ServerBuilderGenerator( self.build_enforcing_all_constraints() } """, - "ReturnType" to buildFnReturnType(), + "ReturnType" to buildFnReturnType(isBuilderFallible, structureSymbol), ) renderBuildEnforcingAllConstraintsFn(implBlockWriter) } private fun renderBuildEnforcingAllConstraintsFn(implBlockWriter: RustWriter) { - implBlockWriter.rustBlockTemplate("fn build_enforcing_all_constraints(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { + implBlockWriter.rustBlockTemplate( + "fn build_enforcing_all_constraints(self) -> #{ReturnType:W}", + "ReturnType" to buildFnReturnType(isBuilderFallible, structureSymbol), + ) { conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { coreBuilder(this) } @@ -414,23 +362,6 @@ class ServerBuilderGenerator( } } - private fun constraintViolations() = members.flatMap { member -> - listOfNotNull( - builderMissingFieldConstraintViolationForMember(member, symbolProvider), - builderConstraintViolationForMember(member), - ) - } - - /** - * Returns the builder failure associated with the `member` field if its target is constrained. - */ - private fun builderConstraintViolationForMember(member: MemberShape) = - if (takeInUnconstrainedTypes && member.targetCanReachConstrainedShape(model, symbolProvider)) { - ConstraintViolation(member, ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE) - } else { - null - } - private fun renderTryFromBuilderImpl(writer: RustWriter) { writer.rustTemplate( """ @@ -516,7 +447,7 @@ class ServerBuilderGenerator( withBlock("$memberName: self.$memberName", ",") { // Write the modifier(s). - builderConstraintViolationForMember(member)?.also { constraintViolation -> + serverBuilderConstraintViolations.builderConstraintViolationForMember(member)?.also { constraintViolation -> val hasBox = builderMemberSymbol(member) .mapRustType { it.stripOuter() } .isRustBoxed() @@ -564,7 +495,7 @@ class ServerBuilderGenerator( } } } - builderMissingFieldConstraintViolationForMember(member, symbolProvider)?.also { + serverBuilderConstraintViolations.forMember(member)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") } } @@ -573,89 +504,10 @@ class ServerBuilderGenerator( } } -/** - * The kinds of constraint violations that can occur when building the builder. - */ -enum class ConstraintViolationKind { - // A field is required but was not provided. - MISSING_MEMBER, - - // An unconstrained type was provided for a field targeting a constrained shape, but it failed to convert into the constrained type. - CONSTRAINED_SHAPE_FAILURE, -} - -data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintViolationKind) { - fun name() = when (kind) { - ConstraintViolationKind.MISSING_MEMBER -> "Missing${forMember.memberName.toPascalCase()}" - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> forMember.memberName.toPascalCase() - } - - /** - * Whether the constraint violation is a Rust tuple struct with one element. - */ - fun hasInner() = kind == ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -} - -/** - * A message for a `ConstraintViolation` variant. This is used in both Rust documentation and the `Display` trait implementation. - */ -fun constraintViolationMessage( - constraintViolation: ConstraintViolation, - symbolProvider: RustSymbolProvider, - structureSymbol: Symbol, -): String { - val memberName = symbolProvider.toMemberName(constraintViolation.forMember) - return when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" - // TODO Nest errors. - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" - } -} - -/** - * Returns the builder failure associated with the [member] field if it is `required`. - */ -fun builderMissingFieldConstraintViolationForMember(member: MemberShape, symbolProvider: RustSymbolProvider) = - // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): See above. - if (symbolProvider.toSymbol(member).isOptional()) { - null +fun buildFnReturnType(isBuilderFallible: Boolean, structureSymbol: Symbol) = writable { + if (isBuilderFallible) { + rust("Result<#T, ConstraintViolation>", structureSymbol) } else { - ConstraintViolation(member, ConstraintViolationKind.MISSING_MEMBER) - } - -fun renderConstraintViolation( - writer: RustWriter, - constraintViolation: ConstraintViolation, - model: Model, - constraintViolationSymbolProvider: SymbolProvider, - symbolProvider: RustSymbolProvider, - structureSymbol: Symbol, -) = - when (constraintViolation.kind) { - ConstraintViolationKind.MISSING_MEMBER -> { - writer.docs( - "${constraintViolationMessage( - constraintViolation, - symbolProvider, - structureSymbol, - ).replaceFirstChar { it.uppercase() }}.", - ) - writer.rust("${constraintViolation.name()},") - } - ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> { - val targetShape = model.expectShape(constraintViolation.forMember.target) - - val constraintViolationSymbol = - constraintViolationSymbolProvider.toSymbol(targetShape) - // If the corresponding structure's member is boxed, box this constraint violation symbol too. - .letIf(constraintViolation.forMember.hasTrait()) { - it.makeRustBoxed() - } - - // Note we cannot express the inner constraint violation as `>::Error`, because `T` might - // be `pub(crate)` and that would leak `T` in a public interface. - writer.docs("${constraintViolationMessage(constraintViolation, symbolProvider, structureSymbol)}.") - Attribute.DocHidden.render(writer) - writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) - } + rust("#T", structureSymbol) } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 453070a780..7ad8cc1542 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.client.rustlang.deprecatedShape @@ -19,8 +18,6 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.rustlang.withBlock -import software.amazon.smithy.rust.codegen.client.rustlang.writable -import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.expectRustMetadata @@ -31,27 +28,33 @@ import software.amazon.smithy.rust.codegen.client.smithy.makeOptional import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType /** - * TODO Docs - * This builder only enforces the `required` trait. + * Generates a builder for the Rust type associated with the [StructureShape]. * - * This builder is always public. + * This builder is similar in design to [ServerBuilderGenerator], so consult its documentation in that regard. However, + * this builder has a few differences. + * + * Unlike [ServerBuilderGenerator], this builder only enforces constraints that are baked into the type system _when + * `publicConstrainedTypes` is false_. So in terms of honoring the Smithy spec, this builder only enforces enums + * and the `required` trait. + * + * Unlike [ServerBuilderGenerator], this builder is always public. It is the only builder type the user is exposed to + * when `publicConstrainedTypes` is false. */ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( codegenContext: ServerCodegenContext, - private val shape: StructureShape, + shape: StructureShape, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider - private val constraintViolationSymbolProvider = - ConstraintViolationSymbolProvider(codegenContext.symbolProvider, model, codegenContext.serviceShape) - private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - // TODO moduleName private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, false) private val moduleName = builderSymbol.namespace.split("::").last() - private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilderWithoutPublicConstrainedTypes(shape, symbolProvider) + private val isBuilderFallible = + StructureGenerator.serverHasFallibleBuilderWithoutPublicConstrainedTypes(shape, symbolProvider) + private val serverBuilderConstraintViolations = + ServerBuilderConstraintViolations(codegenContext, shape, builderTakesInUnconstrainedTypes = false) private val codegenScope = arrayOf( "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), @@ -70,24 +73,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) - writer.docs("Holds one variant for each of the ways the builder can fail.") - val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name - writer.rustBlock("pub enum $constraintViolationSymbolName") { - constraintViolations().forEach { - renderConstraintViolation( - this, - it, - model, - constraintViolationSymbolProvider, - symbolProvider, - structureSymbol, - ) - } - } - - renderImplDisplayConstraintViolation(writer) - writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) + serverBuilderConstraintViolations.render(writer, nonExhaustive = false) renderTryFromBuilderImpl(writer) } else { @@ -95,8 +81,8 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } writer.docs("A builder for #D.", structureSymbol) - // Matching derives to the main structure, - `PartialEq`, + `Default` since we are a builder and everything is optional. - // TODO Manually implement `Default` so that we can add custom docs. + // Matching derives to the main structure, - `PartialEq` (to be consistent with [ServerBuilderGenerator]), + `Default` + // since we are a builder and everything is optional. val baseDerives = structureSymbol.expectRustMetadata().derives val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) @@ -112,34 +98,6 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } } - // TODO Extract - // TODO This impl does not take into account sensitive trait. - private fun renderImplDisplayConstraintViolation(writer: RustWriter) { - writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - rustBlock("match self") { - constraintViolations().forEach { - val arm = if (it.hasInner()) { - "ConstraintViolation::${it.name()}(_)" - } else { - "ConstraintViolation::${it.name()}" - } - rust("""$arm => write!(f, "${constraintViolationMessage(it, symbolProvider, structureSymbol)}"),""") - } - } - } - } - } - - // TODO Extract - private fun buildFnReturnType() = writable { - if (isBuilderFallible) { - rust("Result<#T, ConstraintViolation>", structureSymbol) - } else { - rust("#T", structureSymbol) - } - } - private fun renderBuildFn(implBlockWriter: RustWriter) { implBlockWriter.docs("""Consumes the builder and constructs a #D.""", structureSymbol) if (isBuilderFallible) { @@ -150,15 +108,22 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( structureSymbol, ) } - // TODO Could be single block - implBlockWriter.rustBlockTemplate("pub fn build(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { - rust("self.build_enforcing_required_and_enum_traits()") - } + implBlockWriter.rustTemplate( + """ + pub fn build(self) -> #{ReturnType:W} { + self.build_enforcing_required_and_enum_traits() + } + """, + "ReturnType" to buildFnReturnType(isBuilderFallible, structureSymbol), + ) renderBuildEnforcingRequiredAndEnumTraitsFn(implBlockWriter) } private fun renderBuildEnforcingRequiredAndEnumTraitsFn(implBlockWriter: RustWriter) { - implBlockWriter.rustBlockTemplate("fn build_enforcing_required_and_enum_traits(self) -> #{ReturnType:W}", "ReturnType" to buildFnReturnType()) { + implBlockWriter.rustBlockTemplate( + "fn build_enforcing_required_and_enum_traits(self) -> #{ReturnType:W}", + "ReturnType" to buildFnReturnType(isBuilderFallible, structureSymbol), + ) { conditionalBlock("Ok(", ")", conditional = isBuilderFallible) { coreBuilder(this) } @@ -171,7 +136,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( val memberName = symbolProvider.toMemberName(member) withBlock("$memberName: self.$memberName", ",") { - builderMissingFieldConstraintViolationForMember(member, symbolProvider)?.also { + serverBuilderConstraintViolations.forMember(member)?.also { rust(".ok_or(ConstraintViolation::${it.name()})?") } } @@ -216,10 +181,6 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( } } - private fun constraintViolations() = members.flatMap { member -> - listOfNotNull(builderMissingFieldConstraintViolationForMember(member, symbolProvider)) - } - private fun renderTryFromBuilderImpl(writer: RustWriter) { writer.rustTemplate( """ From 603bf5c2018b1c64ba40b93cb7a2f683cd013a0c Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 19 Sep 2022 16:50:51 +0200 Subject: [PATCH 188/255] Add documentation to `UnconstrainedMapGenerator`, `UnconstrainedUnionGenerator`, `UnconstrainedCollectionGenerator` --- .../generators/UnconstrainedCollectionGenerator.kt | 12 +++++++++++- .../smithy/generators/UnconstrainedMapGenerator.kt | 12 +++++++++++- .../smithy/generators/UnconstrainedUnionGenerator.kt | 12 +++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 5de627a2fc..a72deb11b5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -16,7 +16,17 @@ import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShap import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -// TODO Docs +/** + * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding + * _unconstrained_ shape. This type is a [RustType.Opaque] wrapper tuple newtype holding a `Vec`. Upon request parsing, + * server deserializers use this type to store the incoming values without enforcing the modeled constraints. Only after + * the full request has been parsed are constraints enforced, via the `impl TryFrom for + * ConstrainedSymbol`. + * + * This type is never exposed to the user; it is always `pub(crate)`. Only the deserializers use it. + * + * Consult [UnconstrainedShapeSymbolProvider] for more details and for an example. + */ class UnconstrainedCollectionGenerator( val codegenContext: ServerCodegenContext, private val unconstrainedModuleWriter: RustWriter, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 2ee79f5697..314c2155fc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -19,7 +19,17 @@ import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -// TODO Docs +/** + * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding + * _unconstrained_ shape. This type is a [RustType.Opaque] wrapper tuple newtype holding a `HashMap`. Upon request parsing, + * server deserializers use this type to store the incoming values without enforcing the modeled constraints. Only after + * the full request has been parsed are constraints enforced, via the `impl TryFrom for + * ConstrainedSymbol`. + * + * This type is never exposed to the user; it is always `pub(crate)`. Only the deserializers use it. + * + * Consult [UnconstrainedShapeSymbolProvider] for more details and for an example. + */ class UnconstrainedMapGenerator( val codegenContext: ServerCodegenContext, private val unconstrainedModuleWriter: RustWriter, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index d4ccc5a723..881b966d34 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -32,7 +32,17 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -// TODO Docs +/** + * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ + * shape. This type is a [RustType.Opaque] enum newtype, with each variant holding the corresponding unconstrained type. + * Upon request parsing, server deserializers use this type to store the incoming values without enforcing the modeled + * constraints. Only after the full request has been parsed are constraints enforced, via the `impl + * TryFrom for ConstrainedSymbol`. + * + * This type is never exposed to the user; it is always `pub(crate)`. Only the deserializers use it. + * + * Consult [UnconstrainedShapeSymbolProvider] for more details and for an example. + */ class UnconstrainedUnionGenerator( val codegenContext: ServerCodegenContext, private val unconstrainedModuleWriter: RustWriter, From 8e630178cfe5aa7a87564a6aad0b69d92cb6f593 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 19 Sep 2022 18:23:17 +0200 Subject: [PATCH 189/255] Remove incorrect TODO --- .../server/smithy/generators/UnconstrainedMapGenerator.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 314c2155fc..10d0bc030c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -95,7 +95,6 @@ class UnconstrainedMapGenerator( rustBlock("fn try_from(value: $name) -> Result") { if (isKeyConstrained(keyShape, symbolProvider) || isValueConstrained(valueShape, model, symbolProvider)) { - // TODO I think this breaks if the value shape is a constrained enum (?) Add protocol test. val resolveToNonPublicConstrainedValueType = isValueConstrained(valueShape, model, symbolProvider) && !valueShape.isDirectlyConstrained(symbolProvider) && From 28b3f6f24d4df76967b7c0c62783d8ac2b1dd8b0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 19 Sep 2022 19:26:37 +0200 Subject: [PATCH 190/255] Attach issue to TODO about sensitive Display trait impl in ConstrainedMapGenerator --- .../codegen/server/smithy/generators/ConstrainedMapGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index fb56539119..0c67b153eb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -84,7 +84,7 @@ class ConstrainedMapGenerator( "ConstraintViolation" to constraintViolation, ) - // TODO Display impl missing; it should honor `sensitive` trait. + // TODO(https://github.com/awslabs/smithy-rs/issues/1744): If we end up implementing `Display`, it should honor `sensitive`. writer.documentShape(shape, model, note = rustDocsNote(name)) constrainedTypeMetadata.render(writer) From 3f9478bf259381b3e1871f662e9bcc7fa393671a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 19 Sep 2022 19:54:36 +0200 Subject: [PATCH 191/255] Adjust TODO about Display impl not honoring sensitive trait in ConstrainedStringGenerator --- .../server/smithy/generators/ConstrainedStringGenerator.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index fc4eb67ea8..be0bf269b0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -70,7 +70,8 @@ class ConstrainedStringGenerator( visibility = constrainedTypeVisibility, ) - // TODO Display impl does not honor `sensitive` trait. + // TODO Display impl does not honor `sensitive` trait. Implement it on top of https://github.com/awslabs/smithy-rs/pull/1746 + // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait From e624a534b31bfcb9a381e2260d2c0c0d3d525288 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 21 Sep 2022 13:31:23 +0200 Subject: [PATCH 192/255] Add validation to ensure unsupported constraints are not used And a setting to opt into ignoring. --- .../client/smithy/ServerRustSettings.kt | 4 + codegen-server-test/build.gradle.kts | 9 +- .../server/smithy/ServerCodegenVisitor.kt | 23 +- ...alidateUnsupportedConstraintsAreNotUsed.kt | 194 +++++++++++++++ .../ServerBuilderConstraintViolations.kt | 5 +- .../generators/ServerBuilderGenerator.kt | 1 - ...ateUnsupportedConstraintsAreNotUsedTest.kt | 226 ++++++++++++++++++ 7 files changed, 449 insertions(+), 13 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt index 0fe54fae4b..21e6c9c467 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt @@ -72,6 +72,7 @@ data class ServerRustSettings( /** * [publicConstrainedTypes]: Generate constrained wrapper newtypes for constrained shapes + * [ignoreUnsupportedConstraints]: Generate model even though unsupported constraints are present */ data class ServerCodegenConfig( override val formatTimeoutSeconds: Int = defaultFormatTimeoutSeconds, @@ -79,11 +80,13 @@ data class ServerCodegenConfig( override val eventStreamAllowList: Set = defaultEventStreamAllowList, // TODO Unit test that we don't generate public constrained types when this setting is false. val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes, + val ignoreUnsupportedConstraints: Boolean = defaultIgnoreUnsupportedConstraints, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, eventStreamAllowList, ) { companion object { private const val defaultPublicConstrainedTypes = true + private const val defaultIgnoreUnsupportedConstraints = false fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { @@ -92,6 +95,7 @@ data class ServerCodegenConfig( debugMode = coreCodegenConfig.debugMode, eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, publicConstrainedTypes = node.get().getBooleanMemberOrDefault("publicConstrainedTypes", defaultPublicConstrainedTypes), + ignoreUnsupportedConstraints = node.get().getBooleanMemberOrDefault("ignoreUnsupportedConstraints", defaultIgnoreUnsupportedConstraints), ) } else { ServerCodegenConfig( diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 0f4a1a721d..d276459d4f 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -48,11 +48,16 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> CodegenTest("com.amazonaws.constraints#ConstraintsService", "constraints", imports = listOf("$commonModels/constraints.smithy")), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy")), - CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation"), + CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation", + extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, + ), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), CodegenTest("aws.protocoltests.misc#MiscService", "misc", imports = listOf("$commonModels/misc.smithy")), - CodegenTest("com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json")), + CodegenTest("com.amazonaws.ebs#Ebs", "ebs", + imports = listOf("$commonModels/ebs.json"), + extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, + ), CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), CodegenTest("com.aws.example#PokemonService", "pokemon-service-server-sdk", imports = listOf("$commonModels/pokemon.smithy")), ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 45836c4122..34fdbe828a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.CollectionShape @@ -66,6 +67,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import java.util.logging.Level import java.util.logging.Logger /** @@ -136,11 +138,6 @@ open class ServerCodegenVisitor( rustCrate = RustCrate(context.fileManifest, codegenContext.symbolProvider, DefaultPublicModules, settings.codegenConfig) protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) - - // TODO Traverse the model and error out if: - // * constraint traits on event streams are used. - // * constraint traits on streaming blob are used. - // * constraint trait precedence is used. } /** @@ -183,9 +180,19 @@ open class ServerCodegenVisitor( */ fun execute() { val service = settings.getService(model) - logger.info( + logger.warning( "[rust-server-codegen] Generating Rust server for service $service, protocol ${codegenContext.protocol}", ) + + val validationResult = validateUnsupportedConstraintsAreNotUsed(model, service, codegenContext.settings.codegenConfig) + for (logMessage in validationResult.messages) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1756): These are getting duplicated. + logger.log(logMessage.level, logMessage.message) + } + if (validationResult.shouldAbort) { + throw CodegenException("Unsupported constraints used") + } + val serviceShapes = Walker(model).walkShapes(service) serviceShapes.forEach { it.accept(this) } codegenDecorator.extras(codegenContext, rustCrate) @@ -203,7 +210,7 @@ open class ServerCodegenVisitor( timeout = settings.codegenConfig.formatTimeoutSeconds.toLong(), ) } catch (err: CommandFailed) { - logger.warning( + logger.info( "[rust-server-codegen] Failed to run cargo fmt: [${service.id}]\n${err.output}", ) } @@ -362,7 +369,7 @@ open class ServerCodegenVisitor( IDL v1 spec, but it's unclear what the semantics are. In any case, the Smithy core libraries should enforce the constraints (which it currently does not), not each code generator. See https://github.com/awslabs/smithy/issues/1121f for more information. - """.trimIndent(), + """.trimIndent().replace("\n", " "), ) } else if (!shape.hasTrait() && shape.isDirectlyConstrained(codegenContext.symbolProvider)) { logger.info("[rust-server-codegen] Generating a constrained string $shape") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt new file mode 100644 index 0000000000..3d4d976437 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt @@ -0,0 +1,194 @@ +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.MemberShape +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.UnionShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.model.traits.RequiredTrait +import software.amazon.smithy.model.traits.StreamingTrait +import software.amazon.smithy.model.traits.Trait +import software.amazon.smithy.model.traits.UniqueItemsTrait +import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenConfig +import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.orNull +import java.util.logging.Level + +private sealed class MessageKind { + private val constraintTraitsUberIssue = "https://github.com/awslabs/smithy-rs/issues/1401" + + fun intoLogMessage(ignoreUnsupportedConstraints: Boolean): LogMessage { + fun buildMessage(intro: String, willSupport: Boolean, trackingIssue: String) = + """ + $intro + This is not supported in the smithy-rs server SDK. + ${ if (willSupport) "It will be supported in the future." else "" } + See the tracking issue ($trackingIssue). + If you want to go ahead and generate the server SDK ignoring unsupported constraint traits, set the key `ignoreUnsupportedConstraintTraits` + inside the `runtimeConfig.codegenConfig` JSON object in your `smithy-build.json` to `true`. + """.trimIndent().replace("\n", " ") + + fun buildMessageShapeHasUnsupportedConstraintTrait(shape: Shape, constraintTrait: Trait, trackingIssue: String) = + buildMessage( + "The ${shape.type} shape `${shape.id}` has the constraint trait `${constraintTrait.toShapeId()}` attached.", + willSupport = true, + trackingIssue, + ) + + val level = if (ignoreUnsupportedConstraints) Level.WARNING else Level.SEVERE + + return when (this) { + is UnsupportedConstraintOnMemberShape -> LogMessage( + level, + buildMessageShapeHasUnsupportedConstraintTrait(shape, constraintTrait, constraintTraitsUberIssue) + ) + is UnsupportedConstraintOnShapeReachableViaAnEventStream -> LogMessage( + level, + buildMessage( + """ + The ${shape.type} shape `${shape.id}` has the constraint trait `${constraintTrait.toShapeId()}` attached. + This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are. + """.trimIndent().replace("\n", " "), + willSupport = false, + "https://github.com/awslabs/smithy/issues/1388", + ), + ) + is UnsupportedLengthTraitOnStreamingBlobShape -> LogMessage( + level, + buildMessage( + """ + The ${shape.type} shape `${shape.id}` has both the `${lengthTrait.toShapeId()}` and `${streamingTrait.toShapeId()}` constraint traits attached. + It is unclear what the semantics for streaming blob shapes are. + """.trimIndent().replace("\n", " "), + willSupport = false, + "https://github.com/awslabs/smithy/issues/1389", + ), + ) + is UnsupportedLengthTraitOnCollectionOrOnBlobShape -> LogMessage( + level, + buildMessageShapeHasUnsupportedConstraintTrait(shape, lengthTrait, constraintTraitsUberIssue) + ) + is UnsupportedPatternTraitOnStringShape -> LogMessage( + level, + buildMessageShapeHasUnsupportedConstraintTrait(shape, patternTrait, constraintTraitsUberIssue) + ) + is UnsupportedRangeTraitOnShape -> LogMessage( + level, + buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue) + ) + } + } +} +private data class UnsupportedConstraintOnMemberShape(val shape: MemberShape, val constraintTrait: Trait): MessageKind() +private data class UnsupportedConstraintOnShapeReachableViaAnEventStream(val shape: Shape, val constraintTrait: Trait): MessageKind() +private data class UnsupportedLengthTraitOnStreamingBlobShape(val shape: BlobShape, val lengthTrait: LengthTrait, val streamingTrait: StreamingTrait): MessageKind() +private data class UnsupportedLengthTraitOnCollectionOrOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait): MessageKind() +private data class UnsupportedPatternTraitOnStringShape(val shape: Shape, val patternTrait: PatternTrait): MessageKind() +private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait): MessageKind() + +data class LogMessage(val level: Level, val message: String) +data class ValidationResult(val shouldAbort: Boolean, val messages: List) + +private val allConstraintTraits = setOf( + LengthTrait::class.java, + PatternTrait::class.java, + RangeTrait::class.java, + UniqueItemsTrait::class.java, + EnumTrait::class.java, + RequiredTrait::class.java, +) +private val unsupportedConstraintsOnMemberShapes = allConstraintTraits - RequiredTrait::class.java + +fun validateUnsupportedConstraintsAreNotUsed(model: Model, service: ServiceShape, codegenConfig: ServerCodegenConfig): ValidationResult { + // Traverse the model and error out if: + val walker = Walker(model) + + // 1. Constraint traits on member shapes are used. [Constraint trait precedence] has not been implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + // [Constraint trait precedence]: https://awslabs.github.io/smithy/2.0/spec/model.html#applying-traits + val unsupportedConstraintOnMemberShapeSet = walker + .walkShapes(service) + .asSequence() + .filterIsInstance() + .filterMapShapesToTraits(unsupportedConstraintsOnMemberShapes) + .map { (shape, trait) -> UnsupportedConstraintOnMemberShape(shape as MemberShape, trait) } + .toSet() + + // 2. Constraint traits on streaming blob shapes are used. Their semantics are unclear. + // TODO(https://github.com/awslabs/smithy/issues/1389) + val unsupportedLengthTraitOnStreamingBlobShapeSet = walker + .walkShapes(service) + .asSequence() + .filterIsInstance() + .filter { it.hasTrait() && it.hasTrait() } + .map { UnsupportedLengthTraitOnStreamingBlobShape(it, it.expectTrait(), it.expectTrait()) } + .toSet() + + // 3. Constraint traits in event streams are used. Their semantics are unclear. + // TODO(https://github.com/awslabs/smithy/issues/1388) + val unsupportedConstraintOnShapeReachableViaAnEventStreamSet = walker + .walkShapes(service) + .asSequence() + .filterIsInstance() + .filter { it.hasTrait() } + .flatMap { walker.walkShapes(it) } + .filterMapShapesToTraits(allConstraintTraits) + .map { (shape, trait) -> UnsupportedConstraintOnShapeReachableViaAnEventStream(shape, trait) } + .toSet() + + // 4. Length trait on collection shapes or on blob shapes is used. It has not been implemented yet for these target types. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + val unsupportedLengthTraitOnCollectionOrOnBlobShapeSet = walker + .walkShapes(service) + .asSequence() + .filter { it is CollectionShape || it is BlobShape } + .filter { it.hasTrait() } + .map { UnsupportedLengthTraitOnCollectionOrOnBlobShape(it, it.expectTrait()) } + .toSet() + + // 4. Pattern trait on string shapes is used. It has not been implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + val unsupportedPatternTraitOnStringShapeSet = walker + .walkShapes(service) + .asSequence() + .filterIsInstance() + .filterMapShapesToTraits(setOf(PatternTrait::class.java)) + .map { (shape, patternTrait) -> UnsupportedPatternTraitOnStringShape(shape, patternTrait as PatternTrait) } + .toSet() + + // 5. Range trait on any shape is used. It has not been implemented yet. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + val unsupportedRangeTraitOnShapeSet = walker + .walkShapes(service) + .asSequence() + .filterMapShapesToTraits(setOf(RangeTrait::class.java)) + .map { (shape, rangeTrait) -> UnsupportedRangeTraitOnShape(shape, rangeTrait as RangeTrait) } + .toSet() + + val messages = + unsupportedConstraintOnMemberShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedConstraintOnShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedLengthTraitOnCollectionOrOnBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedPatternTraitOnStringShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) +} + +/** + * Returns a sequence over pairs `(shape, trait)`. + * The returned sequence contains one pair per shape in the input iterable that has attached a trait contained in [traits]. + */ +private fun Sequence.filterMapShapesToTraits(traits: Set>): Sequence> = + this.map { shape -> shape to traits.mapNotNull { shape.getTrait(it).orNull() } } + .flatMap { (shape, traits) -> traits.map { shape to it } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 28b33bf2cf..85557979e2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -83,7 +83,8 @@ class ServerBuilderConstraintViolations( } } - // TODO This impl does not take into account the `sensitive` trait. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) This impl does not take into account the `sensitive` trait. + // When constraint violation error messages are adjusted to match protocol tests, we should ensure it's honored. private fun renderImplDisplayConstraintViolation(writer: RustWriter) { writer.rustBlock("impl #T for ConstraintViolation", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { @@ -160,7 +161,7 @@ data class ConstraintViolation(val forMember: MemberShape, val kind: ConstraintV val structureSymbol = symbolProvider.toSymbol(model.expectShape(forMember.container)) return when (kind) { ConstraintViolationKind.MISSING_MEMBER -> "`$memberName` was not provided but it is required when building `${structureSymbol.name}`" - // TODO Nest errors. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Nest errors. Adjust message following protocol tests. ConstraintViolationKind.CONSTRAINED_SHAPE_FAILURE -> "constraint violation occurred building member `$memberName` when building `${structureSymbol.name}`" } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index e36909754c..e7aff6fcf3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -287,7 +287,6 @@ class ServerBuilderGenerator( if (!constrainedTypeHoldsFinalType(member)) varExpr = "($varExpr).into()" if (wrapInMaybeConstrained) { - // TODO Add a protocol testing the branch (`symbol.isOptional() == false`, `hasBox == true`). conditionalBlock("input.map(##[allow(clippy::redundant_closure)] |v| ", ")", conditional = symbol.isOptional()) { conditionalBlock("Box::new(", ")", conditional = hasBox) { rust("$maybeConstrainedVariant($varExpr)") diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt new file mode 100644 index 0000000000..fd6d505ff9 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -0,0 +1,226 @@ +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.inspectors.forSome +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.collections.shouldHaveAtLeastSize +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenConfig +import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.lookup +import java.util.logging.Level + +internal class ValidateUnsupportedConstraintsAreNotUsedTest { + private val baseModel = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input: TestInputOutput, + output: TestInputOutput, + } + """ + + private fun validateModel(model: Model, serverCodegenConfig: ServerCodegenConfig = ServerCodegenConfig()): ValidationResult { + val service = model.lookup("test#TestService") + return validateUnsupportedConstraintsAreNotUsed(model, service, serverCodegenConfig) + } + + @Test + fun `it should detect when unsupported constraint traits on member shapes are used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + @length(min: 1, max: 69) + lengthString: String + } + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 1 + validationResult.messages[0].message shouldContain "The member shape `test#TestInputOutput\$lengthString` has the constraint trait `smithy.api#length` attached" + } + + @Test + fun `it should not detect when the required trait on a member shape is used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + @required + string: String + } + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 0 + } + + private val constraintTraitOnStreamingBlobShapeModel = + """ + $baseModel + + structure TestInputOutput { + @required + streamingBlob: StreamingBlob + } + + @streaming + @length(min: 69) + blob StreamingBlob + """.asSmithyModel() + + @Test + fun `it should detect when constraint traits on streaming blob shapes are used`() { + val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel) + + validationResult.messages shouldHaveSize 2 + validationResult.messages.forSome { + it.message shouldContain + """ + The blob shape `test#StreamingBlob` has both the `smithy.api#length` and `smithy.api#streaming` constraint traits attached. + It is unclear what the semantics for streaming blob shapes are. + """.trimIndent().replace("\n", " ") + } + } + + @Test + fun `it should detect when constraint traits in event streams are used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + eventStream: EventStream + } + + @streaming + union EventStream { + message: Message + } + + structure Message { + lengthString: LengthString + } + + @length(min: 1) + string LengthString + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 1 + validationResult.messages[0].message shouldContain + """ + The string shape `test#LengthString` has the constraint trait `smithy.api#length` attached. + This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are. + """.trimIndent().replace("\n", " ") + } + + @Test + fun `it should detect when the length trait on collection shapes or on blob shapes is used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + collection: LengthCollection, + blob: LengthBlob + } + + @length(min: 1) + list LengthCollection { + member: String + } + + @length(min: 1) + blob LengthBlob + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 2 + validationResult.messages.forSome { it.message shouldContain "The list shape `test#LengthCollection` has the constraint trait `smithy.api#length` attached" } + validationResult.messages.forSome { it.message shouldContain "The blob shape `test#LengthBlob` has the constraint trait `smithy.api#length` attached" } + } + + @Test + fun `it should detect when the pattern trait on string shapes is used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + patternString: PatternString + } + + @pattern("^[A-Za-z]+$") + string PatternString + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 1 + validationResult.messages[0].message shouldContain "The string shape `test#PatternString` has the constraint trait `smithy.api#pattern` attached" + } + + @Test + fun `it should detect when the range trait is used`() { + val model = + """ + $baseModel + + structure TestInputOutput { + rangeInteger: RangeInteger + } + + @range(min: 1) + integer RangeInteger + """.asSmithyModel() + val validationResult = validateModel(model) + + validationResult.messages shouldHaveSize 1 + validationResult.messages[0].message shouldContain "The integer shape `test#RangeInteger` has the constraint trait `smithy.api#range` attached" + } + + @Test + fun `it should abort when ignoreUnsupportedConstraints is false and unsupported constraints are used`() { + val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig()) + + validationResult.messages shouldHaveAtLeastSize 1 + validationResult.shouldAbort shouldBe true + } + + @Test + fun `it should not abort when ignoreUnsupportedConstraints is true and unsupported constraints are used`() { + val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true)) + + validationResult.messages shouldHaveAtLeastSize 1 + validationResult.shouldAbort shouldBe false + } + + @Test + fun `it should set log level to error when ignoreUnsupportedConstraints is false and unsupported constraints are used`() { + val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig()) + + validationResult.messages shouldHaveAtLeastSize 1 + validationResult.messages.shouldForAll { it.level shouldBe Level.SEVERE } + } + + @Test + fun `it should set log level to warn when ignoreUnsupportedConstraints is true and unsupported constraints are used`() { + val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true)) + + validationResult.messages shouldHaveAtLeastSize 1 + validationResult.messages.shouldForAll { it.level shouldBe Level.WARNING } + } +} From 94ab662911c97b26347e6273693e8d6038d1f979 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 21 Sep 2022 14:19:27 +0200 Subject: [PATCH 193/255] Adjust TODOs --- .../rust/codegen/client/smithy/ServerRustSettings.kt | 1 - .../smithy/generators/MapConstraintViolationGenerator.kt | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt index 21e6c9c467..4aced58fd0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ServerRustSettings.kt @@ -78,7 +78,6 @@ data class ServerCodegenConfig( override val formatTimeoutSeconds: Int = defaultFormatTimeoutSeconds, override val debugMode: Boolean = defaultDebugMode, override val eventStreamAllowList: Set = defaultEventStreamAllowList, - // TODO Unit test that we don't generate public constrained types when this setting is false. val publicConstrainedTypes: Boolean = defaultPublicConstrainedTypes, val ignoreUnsupportedConstraints: Boolean = defaultIgnoreUnsupportedConstraints, ) : CoreCodegenConfig( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 6be27457a6..93994cfc7a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -61,9 +61,10 @@ class MapConstraintViolationGenerator( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), RustMetadata(visibility = constraintViolationVisibility), ) { - // TODO We should really have two `ConstraintViolation` types here. One will just have variants for each - // constraint trait on the map shape, for use by the user. The other one will have variants if the shape's - // key or value is directly or transitively constrained, and is for use by the framework. + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) We should really have two `ConstraintViolation` + // types here. One will just have variants for each constraint trait on the map shape, for use by the user. + // The other one will have variants if the shape's key or value is directly or transitively constrained, + // and is for use by the framework. rustTemplate( """ ##[derive(Debug, PartialEq)] From 274adf155042cde49251a0e6b8842d6f56cd5b6d Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 21 Sep 2022 14:34:53 +0200 Subject: [PATCH 194/255] ./gradlew ktlintFormat --- .../server/smithy/ServerCodegenVisitor.kt | 1 - ...alidateUnsupportedConstraintsAreNotUsed.kt | 32 +++++++++---------- ...ateUnsupportedConstraintsAreNotUsedTest.kt | 8 ++--- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index db58c3ca56..6c00fc57c5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -68,7 +68,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader -import java.util.logging.Level import java.util.logging.Logger /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt index 3d4d976437..abfd575997 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt @@ -49,7 +49,7 @@ private sealed class MessageKind { return when (this) { is UnsupportedConstraintOnMemberShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, constraintTrait, constraintTraitsUberIssue) + buildMessageShapeHasUnsupportedConstraintTrait(shape, constraintTrait, constraintTraitsUberIssue), ) is UnsupportedConstraintOnShapeReachableViaAnEventStream -> LogMessage( level, @@ -75,25 +75,25 @@ private sealed class MessageKind { ) is UnsupportedLengthTraitOnCollectionOrOnBlobShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, lengthTrait, constraintTraitsUberIssue) + buildMessageShapeHasUnsupportedConstraintTrait(shape, lengthTrait, constraintTraitsUberIssue), ) is UnsupportedPatternTraitOnStringShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, patternTrait, constraintTraitsUberIssue) + buildMessageShapeHasUnsupportedConstraintTrait(shape, patternTrait, constraintTraitsUberIssue), ) is UnsupportedRangeTraitOnShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue) + buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue), ) } } } -private data class UnsupportedConstraintOnMemberShape(val shape: MemberShape, val constraintTrait: Trait): MessageKind() -private data class UnsupportedConstraintOnShapeReachableViaAnEventStream(val shape: Shape, val constraintTrait: Trait): MessageKind() -private data class UnsupportedLengthTraitOnStreamingBlobShape(val shape: BlobShape, val lengthTrait: LengthTrait, val streamingTrait: StreamingTrait): MessageKind() -private data class UnsupportedLengthTraitOnCollectionOrOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait): MessageKind() -private data class UnsupportedPatternTraitOnStringShape(val shape: Shape, val patternTrait: PatternTrait): MessageKind() -private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait): MessageKind() +private data class UnsupportedConstraintOnMemberShape(val shape: MemberShape, val constraintTrait: Trait) : MessageKind() +private data class UnsupportedConstraintOnShapeReachableViaAnEventStream(val shape: Shape, val constraintTrait: Trait) : MessageKind() +private data class UnsupportedLengthTraitOnStreamingBlobShape(val shape: BlobShape, val lengthTrait: LengthTrait, val streamingTrait: StreamingTrait) : MessageKind() +private data class UnsupportedLengthTraitOnCollectionOrOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait) : MessageKind() +private data class UnsupportedPatternTraitOnStringShape(val shape: Shape, val patternTrait: PatternTrait) : MessageKind() +private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait) : MessageKind() data class LogMessage(val level: Level, val message: String) data class ValidationResult(val shouldAbort: Boolean, val messages: List) @@ -176,13 +176,13 @@ fun validateUnsupportedConstraintsAreNotUsed(model: Model, service: ServiceShape val messages = unsupportedConstraintOnMemberShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedConstraintOnShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedLengthTraitOnCollectionOrOnBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedPatternTraitOnStringShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedConstraintOnShapeReachableViaAnEventStreamSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedLengthTraitOnCollectionOrOnBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedPatternTraitOnStringShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } - return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) + return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) } /** diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index fd6d505ff9..52458492bb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -89,10 +89,10 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { validationResult.messages shouldHaveSize 2 validationResult.messages.forSome { it.message shouldContain - """ - The blob shape `test#StreamingBlob` has both the `smithy.api#length` and `smithy.api#streaming` constraint traits attached. - It is unclear what the semantics for streaming blob shapes are. - """.trimIndent().replace("\n", " ") + """ + The blob shape `test#StreamingBlob` has both the `smithy.api#length` and `smithy.api#streaming` constraint traits attached. + It is unclear what the semantics for streaming blob shapes are. + """.trimIndent().replace("\n", " ") } } From defe1fee8e68dd1f773e7ea2f3f33ab831a8e47a Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 27 Sep 2022 11:18:39 +0200 Subject: [PATCH 195/255] save work -- was trying to get rest_json_validation to compile --- .../common-test-models/constraints.smithy | 75 ++++++++++--------- .../server/smithy/ServerRuntimeType.kt | 4 + .../generators/ConstrainedStringGenerator.kt | 8 +- .../ServerBuilderConstraintViolations.kt | 41 ++++++++++ .../generators/ServerBuilderGenerator.kt | 15 +++- .../protocol/ServerProtocolTestGenerator.kt | 3 - ...ateUnsupportedConstraintsAreNotUsedTest.kt | 22 ++++++ .../aws-smithy-http-server/Cargo.toml | 2 + .../aws-smithy-http-server/src/rejection.rs | 9 ++- .../src/runtime_error.rs | 39 +++++++--- 10 files changed, 162 insertions(+), 56 deletions(-) diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index d4ac551cc2..66ef678bf1 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -3,6 +3,7 @@ $version: "1.0" namespace com.amazonaws.constraints use aws.protocols#restJson1 +use smithy.framework#ValidationException /// A service to test aspects of code generation where shapes have constraint traits. @restJson1 @@ -10,25 +11,25 @@ use aws.protocols#restJson1 service ConstraintsService { operations: [ ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfEnumStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - QueryParamsTargetingMapOfListOfEnumStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // HttpPrefixHeadersTargetingMapOfEnumStringOperation, - - NonStreamingBlobOperation, - - StreamingBlobOperation, - EventStreamsOperation, +// ConstrainedHttpBoundShapesOperation, +// ConstrainedRecursiveShapesOperation, +// // `httpQueryParams` and `httpPrefixHeaders` are structurually +// // exclusive, so we need one operation per target shape type +// // combination. +// QueryParamsTargetingLengthMapOperation, +// QueryParamsTargetingMapOfLengthStringOperation, +// QueryParamsTargetingMapOfEnumStringOperation, +// QueryParamsTargetingMapOfListOfLengthStringOperation, +// QueryParamsTargetingMapOfSetOfLengthStringOperation, +// QueryParamsTargetingMapOfListOfEnumStringOperation, +// HttpPrefixHeadersTargetingLengthMapOperation, +// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) +// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + +// NonStreamingBlobOperation, + +// StreamingBlobOperation, +// EventStreamsOperation, ], } @@ -36,7 +37,7 @@ service ConstraintsService { operation ConstrainedShapesOperation { input: ConstrainedShapesOperationInputOutput, output: ConstrainedShapesOperationInputOutput, - errors: [ErrorWithLengthStringMessage] + errors: [ValidationException] } @http(uri: "/constrained-http-bound-shapes-operation/{lengthStringLabel}/{enumStringLabel}", method: "POST") @@ -252,33 +253,33 @@ blob StreamingBlob blob NonStreamingBlob structure ConA { - @required - conB: ConB, +// @required +// conB: ConB, - optConB: ConB, +// optConB: ConB, lengthString: LengthString, - minLengthString: MinLengthString, - maxLengthString: MaxLengthString, - fixedLengthString: FixedLengthString, +// minLengthString: MinLengthString, +// maxLengthString: MaxLengthString, +// fixedLengthString: FixedLengthString, - conBList: ConBList, - conBList2: ConBList2, +// conBList: ConBList, +// conBList2: ConBList2, - conBSet: ConBSet, +// conBSet: ConBSet, - conBMap: ConBMap, +// conBMap: ConBMap, - mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, +// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, - constrainedUnion: ConstrainedUnion, - enumString: EnumString, +// constrainedUnion: ConstrainedUnion, +// enumString: EnumString, - listOfLengthString: ListOfLengthString, - setOfLengthString: SetOfLengthString, - mapOfLengthString: MapOfLengthString, +// listOfLengthString: ListOfLengthString, +// setOfLengthString: SetOfLengthString, +// mapOfLengthString: MapOfLengthString, - nonStreamingBlob: NonStreamingBlob +// nonStreamingBlob: NonStreamingBlob } map MapOfLengthString { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index fa386b8887..b28671ab73 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -33,6 +33,10 @@ object ServerRuntimeType { fun RuntimeError(runtimeConfig: RuntimeConfig) = RuntimeType("RuntimeError", ServerCargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::runtime_error") + fun RejectionModule(runtimeConfig: RuntimeConfig) = + RuntimeType("rejection", ServerCargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server") + + // TODO Reuse above fun RequestRejection(runtimeConfig: RuntimeConfig) = RuntimeType("RequestRejection", ServerCargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::rejection") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index be0bf269b0..589a572913 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -104,7 +104,7 @@ class ConstrainedStringGenerator( type Unconstrained = $inner; } - impl From<$inner> for #{MaybeConstrained} { + impl #{From}<$inner> for #{MaybeConstrained} { fn from(value: $inner) -> Self { Self::Unconstrained(value) } @@ -151,6 +151,12 @@ class ConstrainedStringGenerator( pub enum ${constraintViolation.name} { Length(usize), } + + impl ${constraintViolation.name} { + pub(crate) fn as_validation_exception_field_message(self, _path: &str) -> String { + "Value with length 1 at '/string' failed to satisfy constraint: Member must have length between 2 and 8, inclusive".to_owned() + } + } """, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 85557979e2..bce124011c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -9,6 +9,8 @@ import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.docs import software.amazon.smithy.rust.codegen.client.rustlang.rust import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.client.rustlang.writable import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext @@ -58,6 +60,9 @@ class ServerBuilderConstraintViolations( } renderImplDisplayConstraintViolation(writer) writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) + + // TODO This is only needed if the structure shape is part of an operation input closure. + renderAsValidationExceptionFieldList(writer) } /** @@ -129,6 +134,42 @@ class ServerBuilderConstraintViolations( } } } + + private fun renderAsValidationExceptionFieldList(writer: RustWriter) { + val validationExceptionFieldMessageWritable = writable { + rustBlock("match self") { + all.forEach { + val arm = if (it.hasInner()) { + "ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field_message(path)," + } else { + """ConstraintViolation::${it.name()} => format!("Value null at '{}' failed to satisfy constraint: Member must not be null", path),""" + } + rust(arm) + } + } + } + + // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. + writer.rustTemplate( + """ + impl ConstraintViolation { + ##[allow(dead_code)] + pub(crate) fn as_validation_exception_field_message(self, path: &str) -> String { + #{ValidationExceptionFieldMessageWritable:W} + } + + ##[allow(dead_code)] + pub(crate) fn as_validation_exception_field(self, path: &str) -> crate::model::ValidationExceptionField { + crate::model::ValidationExceptionField { + path: path.to_owned(), + message: self.as_validation_exception_field_message(path), + } + } + } + """, + "ValidationExceptionFieldMessageWritable" to validationExceptionFieldMessageWritable, + ) + } } /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index e7aff6fcf3..c0ef296310 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -89,6 +89,7 @@ class ServerBuilderGenerator( ) { private val takeInUnconstrainedTypes = shape.isReachableFromOperationInput() private val model = codegenContext.model + private val runtimeConfig = codegenContext.runtimeConfig private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val symbolProvider = codegenContext.symbolProvider private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider @@ -103,7 +104,7 @@ class ServerBuilderGenerator( ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes) private val codegenScope = arrayOf( - "RequestRejection" to ServerRuntimeType.RequestRejection(codegenContext.runtimeConfig), + "RequestRejection" to ServerRuntimeType.RequestRejection(runtimeConfig), "Structure" to structureSymbol, "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, @@ -170,8 +171,16 @@ class ServerBuilderGenerator( writer.rustTemplate( """ impl #{From} for #{RequestRejection} { - fn from(value: ConstraintViolation) -> Self { - Self::Build(value.into()) + fn from(constraint_violation: ConstraintViolation) -> Self { + let first_validation_exception_field = constraint_violation.as_validation_exception_field("/string"); + let validation_exception = crate::error::ValidationException { + message: format!("1 validation error detected. {}", &first_validation_exception_field.message), + field_list: Some(vec![first_validation_exception_field]), + }; + Self::ConstraintViolation( + crate::operation_ser::serialize_structure_crate_error_validation_exception(&validation_exception) + .expect("impossible") + ) } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 53a5a05b3f..eef6b02d02 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -886,9 +886,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapKey_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthString_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthString_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthString_case2", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case0", TestType.MalformedRequest), diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 52458492bb..406a867e2c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -8,6 +8,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel @@ -35,6 +36,27 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { return validateUnsupportedConstraintsAreNotUsed(model, service, serverCodegenConfig) } + @Test + fun `foo`() { + val model = + """ + $baseModel + + structure TestInputOutput { + string: String + } + + @length(min: 1, max: 100) + string LengthString + """.asSmithyModel() + + val service = model.lookup("test#TestService") + val s = Walker(model).walkShapes(service).toSet() + for (member in s) { + println(member) + } + } + @Test fun `it should detect when unsupported constraint traits on member shapes are used`() { val model = diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 055b7aab8c..4a73be43cf 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -32,6 +32,8 @@ nom = "7" pin-project-lite = "0.2" once_cell = "1.13" regex = "1.5.5" +serde = "1.0" +serde_json = "1.0" serde_urlencoded = "0.7" strum_macros = "0.24" thiserror = "1" diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 71d0c3bcac..a06b4e1d03 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -179,15 +179,18 @@ pub enum RequestRejection { FloatParse(crate::Error), BoolParse(crate::Error), - /// Used when consuming the input struct builder, and a constraint violation occurs. + /// Used when consuming the input struct builder, and constraint violations occur. // Unlike the rejections above, this does not take in `crate::Error`, since it is constructed // directly in the code-generated SDK instead of in this crate. - Build(Box), + // TODO(https://github.com/awslabs/smithy-rs/issues/1703): this will hold a type that can be + // rendered into a protocol-specific response later on. + ConstraintViolation(String), + // TODO ConstraintViolation should supersede this variant. /// Used by the server when the enum variant sent by a client is not known. // Unlike the rejections above, the inner type is code generated, // with each enum having its own generated error type. - EnumVariantNotFound(Box), + EnumVariantNotFound(crate::error::BoxError), } #[derive(Debug, Display)] diff --git a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs index f86b6df6b4..0369b5d126 100644 --- a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs @@ -21,6 +21,8 @@ //! and converts into the corresponding `RuntimeError`, and then it uses the its //! [`RuntimeError::into_response`] method to render and send a response. +use std::borrow::Cow; + use crate::{ protocols::{AwsJson10, AwsJson11, AwsRestJson1, AwsRestXml, Protocol}, response::{IntoResponse, Response}, @@ -36,6 +38,10 @@ pub enum RuntimeErrorKind { // TODO(https://github.com/awslabs/smithy-rs/issues/1663) NotAcceptable, UnsupportedMediaType, + + // TODO(https://github.com/awslabs/smithy-rs/issues/1703): this will hold a type that can be + // rendered into a protocol-specific response later on. + Validation(String), } /// String representation of the runtime error type. @@ -48,6 +54,7 @@ impl RuntimeErrorKind { RuntimeErrorKind::InternalFailure(_) => "InternalFailureException", RuntimeErrorKind::NotAcceptable => "NotAcceptableException", RuntimeErrorKind::UnsupportedMediaType => "UnsupportedMediaTypeException", + RuntimeErrorKind::Validation(_) => "ValidationException", } } } @@ -104,17 +111,9 @@ impl RuntimeError { RuntimeErrorKind::InternalFailure(_) => http::StatusCode::INTERNAL_SERVER_ERROR, RuntimeErrorKind::NotAcceptable => http::StatusCode::NOT_ACCEPTABLE, RuntimeErrorKind::UnsupportedMediaType => http::StatusCode::UNSUPPORTED_MEDIA_TYPE, + RuntimeErrorKind::Validation(_) => http::StatusCode::BAD_REQUEST, }; - let body = crate::body::to_boxed(match self.protocol { - Protocol::RestJson1 => "{}", - Protocol::RestXml => "", - // See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#empty-body-serialization - Protocol::AwsJson10 => "", - // See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html#empty-body-serialization - Protocol::AwsJson11 => "", - }); - let mut builder = http::Response::builder(); builder = builder.status(status_code); @@ -133,6 +132,27 @@ impl RuntimeError { self.kind.name(), ))); + let body = crate::body::to_boxed(match self.kind { + RuntimeErrorKind::Validation(reason) => Cow::Owned(match self.protocol { + Protocol::RestJson1 | Protocol::AwsJson10 | Protocol::AwsJson11 => { + // https://docs.rs/serde_json/latest/serde_json/ser/fn.to_string.html#errors + // serde_json::to_string(reason).expect("unexpected failure during serialization of constraint violation; please file a bug report under https://github.com/awslabs/smithy-rs/issues") + reason + } + Protocol::RestXml => todo!(), + }), + _ => { + Cow::Borrowed(match self.protocol { + Protocol::RestJson1 => "{}", + Protocol::RestXml => "", + // See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#empty-body-serialization + Protocol::AwsJson10 => "", + // See https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_1-protocol.html#empty-body-serialization + Protocol::AwsJson11 => "", + }) + } + }); + builder.body(body).expect("invalid HTTP response for `RuntimeError`; please file a bug report under https://github.com/awslabs/smithy-rs/issues") } } @@ -153,6 +173,7 @@ impl From for RuntimeErrorKind { fn from(err: crate::rejection::RequestRejection) -> Self { match err { crate::rejection::RequestRejection::MissingContentType(_reason) => RuntimeErrorKind::UnsupportedMediaType, + crate::rejection::RequestRejection::ConstraintViolation(reason) => RuntimeErrorKind::Validation(reason), _ => RuntimeErrorKind::Serialization(crate::Error::new(err)), } } From 050e225f366a9c7cd1cbea967277b467ec684c06 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 12:19:44 +0200 Subject: [PATCH 196/255] passes protocol tests --- .../generators/ConstrainedStringGenerator.kt | 31 +++++++++++++++---- .../MapConstraintViolationGenerator.kt | 26 ++++++++++++++++ .../ServerBuilderConstraintViolations.kt | 30 +++++++++--------- .../generators/ServerBuilderGenerator.kt | 2 +- .../smithy/generators/ServerEnumGenerator.kt | 10 ++++++ .../UnconstrainedCollectionGenerator.kt | 6 ++++ .../generators/UnconstrainedUnionGenerator.kt | 10 ++++++ .../protocol/ServerProtocolTestGenerator.kt | 6 ---- 8 files changed, 92 insertions(+), 29 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 589a572913..d7e3c9c82f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.rustlang.Visibility import software.amazon.smithy.rust.codegen.client.rustlang.documentShape import software.amazon.smithy.rust.codegen.client.rustlang.render import software.amazon.smithy.rust.codegen.client.rustlang.rust +import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext @@ -151,14 +152,32 @@ class ConstrainedStringGenerator( pub enum ${constraintViolation.name} { Length(usize), } - - impl ${constraintViolation.name} { - pub(crate) fn as_validation_exception_field_message(self, _path: &str) -> String { - "Value with length 1 at '/string' failed to satisfy constraint: Member must have length between 2 and 8, inclusive".to_owned() - } - } """, ) + + rustBlock("impl ${constraintViolation.name}") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("match self") { + val beginning = "Value with length {} at '{}' failed to satisfy constraint: Member must have length " + val ending = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { + "between ${lengthTrait.min.get()} and ${lengthTrait.max.get()}, inclusive" + } else if (lengthTrait.min.isPresent) ( + "greater than or equal to ${lengthTrait.min.get()}" + ) else { + check(lengthTrait.max.isPresent) + "less than or equal to ${lengthTrait.max.get()}" + } + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("$beginning$ending", length, &path), + path, + }, + """ + ) + } + } + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 93994cfc7a..5b99135012 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -11,6 +11,8 @@ import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.Visibility +import software.amazon.smithy.rust.codegen.client.rustlang.rust +import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -76,6 +78,30 @@ class MapConstraintViolationGenerator( """, *constraintViolationCodegenScope, ) + + rustBlock("impl $constraintViolationName") { + // TODO Remove `dead_code` once we use `path`. + rustBlock("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("match self") { + if (shape.hasTrait()) { + // TODO + rust( + """ + Self::Length(_length) => crate::model::ValidationExceptionField { + path, + message: "make code good".to_owned(), + }, + """) + } + if (isKeyConstrained(keyShape, symbolProvider)) { + rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path + "/${shape.key.memberName}"),""") + } + if (isValueConstrained(valueShape, model, symbolProvider)) { + rust("""Self::Value(value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "${shape.value.memberName}"),""") + } + } + } + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index bce124011c..a1fe4582c7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -136,15 +136,21 @@ class ServerBuilderConstraintViolations( } private fun renderAsValidationExceptionFieldList(writer: RustWriter) { - val validationExceptionFieldMessageWritable = writable { + val validationExceptionFieldWritable = writable { rustBlock("match self") { all.forEach { - val arm = if (it.hasInner()) { - "ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field_message(path)," + if (it.hasInner()) { + rust("""ConstraintViolation::${it.name()}(inner) => inner.as_validation_exception_field(path + "/${it.forMember.memberName}"),""") } else { - """ConstraintViolation::${it.name()} => format!("Value null at '{}' failed to satisfy constraint: Member must not be null", path),""" + rust( + """ + ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField { + path: format!("{}/${it.forMember.memberName}", path), + message: format!("Value null at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), + }, + """ + ) } - rust(arm) } } } @@ -154,20 +160,12 @@ class ServerBuilderConstraintViolations( """ impl ConstraintViolation { ##[allow(dead_code)] - pub(crate) fn as_validation_exception_field_message(self, path: &str) -> String { - #{ValidationExceptionFieldMessageWritable:W} - } - - ##[allow(dead_code)] - pub(crate) fn as_validation_exception_field(self, path: &str) -> crate::model::ValidationExceptionField { - crate::model::ValidationExceptionField { - path: path.to_owned(), - message: self.as_validation_exception_field_message(path), - } + pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { + #{ValidationExceptionFieldWritable:W} } } """, - "ValidationExceptionFieldMessageWritable" to validationExceptionFieldMessageWritable, + "ValidationExceptionFieldWritable" to validationExceptionFieldWritable, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index c0ef296310..023798f501 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -172,7 +172,7 @@ class ServerBuilderGenerator( """ impl #{From} for #{RequestRejection} { fn from(constraint_violation: ConstraintViolation) -> Self { - let first_validation_exception_field = constraint_violation.as_validation_exception_field("/string"); + let first_validation_exception_field = constraint_violation.as_validation_exception_field("".to_owned()); let validation_exception = crate::error::ValidationException { message: format!("1 validation error detected. {}", &first_validation_exception_field.message), field_list: Some(vec![first_validation_exception_field]), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 231e67f954..ec6a190c98 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -40,10 +40,20 @@ open class ServerEnumGenerator( writer.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), ) { + // TODO as_validation_exception_field message rustTemplate( """ ##[derive(Debug, PartialEq)] pub struct $constraintViolationName(pub(crate) #{String}); + + impl $constraintViolationName { + pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { + crate::model::ValidationExceptionField { + message: "unknown enum variant blah blah".to_owned(), + path, + } + } + } """, "String" to RuntimeType.String, ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index a72deb11b5..f6d1e104d6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -108,6 +108,12 @@ class UnconstrainedCollectionGenerator( """ ##[derive(Debug, PartialEq)] pub struct $constraintViolationName(pub(crate) #{InnerConstraintViolationSymbol}); + + impl $constraintViolationName { + pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { + self.0.as_validation_exception_field(path) + } + } """, "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 881b966d34..66531a3493 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -138,6 +138,16 @@ class UnconstrainedUnionGenerator( rustBlock("pub enum $constraintViolationName") { constraintViolations().forEach { renderConstraintViolation(this, it) } } + + rustBlock("impl $constraintViolationName") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + withBlock("match self {", "}") { + for (constraintViolation in constraintViolations()) { + rust("""Self::${constraintViolation.name()}(inner) => inner.as_validation_exception_field(path + "/${constraintViolation.forMember.memberName}"),""") + } + } + } + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index eef6b02d02..0666d49c0c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -917,9 +917,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedRangeFloat_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthMaxStringOverride", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthMinStringOverride", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthQueryStringNoValue", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMaxString", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMinString", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxByteOverride", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxFloatOverride", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinByteOverride", TestType.MalformedRequest), @@ -928,9 +925,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxFloat", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinByte", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinFloat", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRequiredBodyExplicitNull", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRequiredBodyUnset", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRequiredHeaderUnset", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRecursiveStructures", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternSensitiveString", TestType.MalformedRequest), From 4cad83e86a45e8f8879ddb591ff182b01b70332d Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 13:08:45 +0200 Subject: [PATCH 197/255] fix length map and length map key validation messages --- .../generators/ConstrainedStringGenerator.kt | 12 ++---------- .../MapConstraintViolationGenerator.kt | 19 +++++++++++-------- .../protocol/ServerProtocolTestGenerator.kt | 16 ---------------- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index d7e3c9c82f..07d3fca486 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage /** * [ConstrainedStringGenerator] generates a wrapper tuple newtype holding a constrained `String`. @@ -158,19 +159,10 @@ class ConstrainedStringGenerator( rustBlock("impl ${constraintViolation.name}") { rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { rustBlock("match self") { - val beginning = "Value with length {} at '{}' failed to satisfy constraint: Member must have length " - val ending = if (lengthTrait.min.isPresent && lengthTrait.max.isPresent) { - "between ${lengthTrait.min.get()} and ${lengthTrait.max.get()}, inclusive" - } else if (lengthTrait.min.isPresent) ( - "greater than or equal to ${lengthTrait.min.get()}" - ) else { - check(lengthTrait.max.isPresent) - "less than or equal to ${lengthTrait.max.get()}" - } rust( """ Self::Length(length) => crate::model::ValidationExceptionField { - message: format!("$beginning$ending", length, &path), + message: format!("${lengthTrait.validationErrorMessage()}", length, &path), path, }, """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 5b99135012..e7a70c37dd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -15,8 +15,10 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rust import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage class MapConstraintViolationGenerator( codegenContext: ServerCodegenContext, @@ -80,24 +82,25 @@ class MapConstraintViolationGenerator( ) rustBlock("impl $constraintViolationName") { - // TODO Remove `dead_code` once we use `path`. - rustBlock("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { rustBlock("match self") { - if (shape.hasTrait()) { - // TODO + shape.getTrait()?.also { rust( """ - Self::Length(_length) => crate::model::ValidationExceptionField { + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.validationErrorMessage()}", length, &path), path, - message: "make code good".to_owned(), }, """) } if (isKeyConstrained(keyShape, symbolProvider)) { - rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path + "/${shape.key.memberName}"),""") + // Note how we _do not_ append the key's member name to the path. This is intentional, as + // per the `RestJsonMalformedLengthMapKey` test. Note keys are always strings. + // https://github.com/awslabs/smithy/blob/ee0b4ff90daaaa5101f32da936c25af8c91cc6e9/smithy-aws-protocol-tests/model/restJson1/validation/malformed-length.smithy#L296-L295 + rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") } if (isValueConstrained(valueShape, model, symbolProvider)) { - rust("""Self::Value(value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "${shape.value.memberName}"),""") + rust("""Self::Value(value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/${shape.value.memberName}"),""") } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 0666d49c0c..a35147b72f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -855,16 +855,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJson, "RestJsonMalformedUnionNoFieldsSet", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonMalformedSetDuplicateBlobs", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumList_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapKey_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapKey_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapValue_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumMapValue_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthListOverride_case0", TestType.MalformedRequest), @@ -880,12 +870,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedLengthList_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthListValue_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthListValue_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMap_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMap_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapKey_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapKey_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case0", TestType.MalformedRequest), From 169e6de960cb5bfb4bac1331800a174a612978f6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 13:35:18 +0200 Subject: [PATCH 198/255] fix enum and recursive structures test --- .../client/smithy/generators/EnumGenerator.kt | 2 +- .../LengthTraitValidationErrorMessage.kt | 16 ++++++++++ .../smithy/generators/ServerEnumGenerator.kt | 30 +++++++++++++------ .../protocol/ServerProtocolTestGenerator.kt | 1 - 4 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EnumGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EnumGenerator.kt index 76ec77d759..6e9a1e21ad 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EnumGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EnumGenerator.kt @@ -83,7 +83,7 @@ open class EnumGenerator( private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, protected val shape: StringShape, - private val enumTrait: EnumTrait, + protected val enumTrait: EnumTrait, ) { protected val symbol = symbolProvider.toSymbol(shape) protected val enumName = symbol.name diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt new file mode 100644 index 0000000000..de3fa15ca3 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt @@ -0,0 +1,16 @@ +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.model.traits.LengthTrait + +fun LengthTrait.validationErrorMessage(): String { + val beginning = "Value with length {} at '{}' failed to satisfy constraint: Member must have length " + val ending = if (this.min.isPresent && this.max.isPresent) { + "between ${this.min.get()} and ${this.max.get()}, inclusive" + } else if (this.min.isPresent) ( + "greater than or equal to ${this.min.get()}" + ) else { + check(this.max.isPresent) + "less than or equal to ${this.max.get()}" + } + return "$beginning$ending" +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index ec6a190c98..740d1a2cb0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -8,6 +8,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.rust import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.client.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext @@ -35,28 +36,39 @@ open class ServerEnumGenerator( } private val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) private val constraintViolationName = constraintViolationSymbol.name + private val codegenScope = arrayOf( + "String" to RuntimeType.String, + ) override fun renderFromForStr() { writer.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), ) { - // TODO as_validation_exception_field message + // TODO Check that we're using `#{String}` in the other as_validation_exception_field methods. rustTemplate( """ ##[derive(Debug, PartialEq)] pub struct $constraintViolationName(pub(crate) #{String}); - - impl $constraintViolationName { - pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { + """, + *codegenScope, + ) + + // TODO Move out. + val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") + val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" + + rustBlock("impl $constraintViolationName") { + rustBlockTemplate("pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { + rust( + """ crate::model::ValidationExceptionField { - message: "unknown enum variant blah blah".to_owned(), + message: format!("$message", &self.0, &path), path, } - } + """ + ) } - """, - "String" to RuntimeType.String, - ) + } } writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { rust("type Error = #T;", constraintViolationSymbol) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index a35147b72f..0136dbde6b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -909,7 +909,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedRangeMaxFloat", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinByte", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedRangeMinFloat", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedRecursiveStructures", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternSensitiveString", TestType.MalformedRequest), // Some tests for the S3 service (restXml). From 8af068d3770911a0ba183f14f434e25ea3c76df1 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 14:28:04 +0200 Subject: [PATCH 199/255] fix list value test cases --- .../generators/UnconstrainedCollectionGenerator.kt | 14 +++++++++----- .../protocol/ServerProtocolTestGenerator.kt | 2 -- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index f6d1e104d6..63497bf760 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -76,13 +76,14 @@ class UnconstrainedCollectionGenerator( type Error = #{ConstraintViolationSymbol}; fn try_from(value: $name) -> Result { - let res: Result<_, #{InnerConstraintViolationSymbol}> = value + let res: Result<_, (usize, #{InnerConstraintViolationSymbol})> = value .0 .into_iter() - .map(|inner| inner.try_into()) + .enumerate() + .map(|(idx, inner)| inner.try_into().map_err(|inner_violation| (idx, inner_violation))) .collect(); res.map(Self) - .map_err(#{ConstraintViolationSymbol}) + .map_err(|(idx, inner_violation)| #{ConstraintViolationSymbol}(idx, inner_violation)) } } """, @@ -107,11 +108,14 @@ class UnconstrainedCollectionGenerator( rustTemplate( """ ##[derive(Debug, PartialEq)] - pub struct $constraintViolationName(pub(crate) #{InnerConstraintViolationSymbol}); + pub struct $constraintViolationName( + pub(crate) usize, + pub(crate) #{InnerConstraintViolationSymbol} + ); impl $constraintViolationName { pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { - self.0.as_validation_exception_field(path) + self.1.as_validation_exception_field(format!("{}/{}", path, self.0)) } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 0136dbde6b..4a1cb2fc4d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -868,8 +868,6 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlob_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthList_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthList_case1", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthListValue_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedLengthListValue_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case0", TestType.MalformedRequest), From 3cbf2cf7a5df3d783d85609f00735ca3e197c5fd Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 16:30:00 +0200 Subject: [PATCH 200/255] fix rest_json_malformed_enum_map --- .../generators/ConstrainedStringGenerator.kt | 5 ++++ .../MapConstraintViolationGenerator.kt | 23 ++++++++----------- .../UnconstrainedCollectionGenerator.kt | 1 + .../generators/UnconstrainedMapGenerator.kt | 5 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 07d3fca486..6885012f54 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -91,6 +91,11 @@ class ConstrainedStringGenerator( Self::try_from(value) } + /// Extracts a string slice containing the entire underlying `String`. + pub fn as_str(&self) -> &str { + &self.0 + } + /// ${rustDocsInnerMethod(inner)} pub fn inner(&self) -> &$inner { &self.0 diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index e7a70c37dd..a20624715b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -26,6 +26,7 @@ class MapConstraintViolationGenerator( val shape: MapShape, ) { private val model = codegenContext.model + private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val symbolProvider = codegenContext.symbolProvider private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes private val constraintViolationSymbolProvider = @@ -43,18 +44,11 @@ class MapConstraintViolationGenerator( val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbol.name - val constraintViolationCodegenScope = listOfNotNull( - if (isKeyConstrained(keyShape, symbolProvider)) { - "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape) - } else { - null - }, - if (isValueConstrained(valueShape, model, symbolProvider)) { - "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape) - } else { - null - }, - ).toTypedArray() + val constraintViolationCodegenScope = arrayOf( + "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape), + "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape), + "KeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape), + ) val constraintViolationVisibility = if (publicConstrainedTypes) { Visibility.PUBLIC @@ -75,7 +69,7 @@ class MapConstraintViolationGenerator( pub enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} ${if (isKeyConstrained(keyShape, symbolProvider)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} - ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{ValueConstraintViolationSymbol})," else ""} + ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} } """, *constraintViolationCodegenScope, @@ -100,7 +94,8 @@ class MapConstraintViolationGenerator( rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") } if (isValueConstrained(valueShape, model, symbolProvider)) { - rust("""Self::Value(value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/${shape.value.memberName}"),""") + // `as_str()` works with regular `String`s and constrained string shapes. + rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 63497bf760..5dd59a44fe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -105,6 +105,7 @@ class UnconstrainedCollectionGenerator( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), RustMetadata(visibility = constraintViolationVisibility), ) { + // TODO path + "/" + &self.0 instead of format! rustTemplate( """ ##[derive(Debug, PartialEq)] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 10d0bc030c..652e34b11b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -105,13 +105,14 @@ class UnconstrainedMapGenerator( constrainedShapeSymbolProvider.toSymbol(valueShape) } + // TODO Refactor to use `return` so that we don't have to clone `k`. rustTemplate( """ let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { - ${if (isKeyConstrained(keyShape, symbolProvider)) "let k = k.try_into().map_err(Self::Error::Key)?;" else ""} - ${if (isValueConstrained(valueShape, model, symbolProvider)) "let v = v.try_into().map_err(Self::Error::Value)?;" else ""} + ${if (isKeyConstrained(keyShape, symbolProvider)) "let k: #{ConstrainedKeySymbol} = k.try_into().map_err(Self::Error::Key)?;" else ""} + ${if (isValueConstrained(valueShape, model, symbolProvider)) "let v: #{ConstrainedValueSymbol} = v.try_into().map_err(|inner_violation| Self::Error::Value(k.clone(), inner_violation))?;" else ""} Ok((k, v)) }) .collect(); From 42cb88e6dcfec234ed13b71146141658a286dc38 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 28 Sep 2022 16:44:18 +0200 Subject: [PATCH 201/255] bring back LengthMapValue tests; we haven't implemented range on list shapes yet --- .../smithy/generators/protocol/ServerProtocolTestGenerator.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 4a1cb2fc4d..ef492c2cbf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -868,6 +868,8 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlob_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthList_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthList_case1", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedLengthMapValue_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternListOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedPatternMapKeyOverride_case0", TestType.MalformedRequest), From 925bd700d749f8edbae01ae3fb7102c146498cf9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:00:47 +0200 Subject: [PATCH 202/255] Link to 2.0 spec in Constraints.kt https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977716563 --- .../amazon/smithy/rust/codegen/client/smithy/Constraints.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt index 0d23ef7ef1..2610ba065c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt @@ -41,9 +41,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's * (with these subsets being smaller than what [the spec] accounts for). * - * Note `uniqueItems` is deprecated, so we won't ever implement it. - * - * [the spec]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html + * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html */ fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { is StructureShape -> { From b421e0509e24f5f56cec373c0500eb1fc1b56faf Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:02:27 +0200 Subject: [PATCH 203/255] Explicitly type `Boolean` return type in `Constraints.kt` https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977719443 --- .../smithy/rust/codegen/client/smithy/Constraints.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt index 2610ba065c..40c2810435 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/Constraints.kt @@ -43,7 +43,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait * * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html */ -fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider) = when (this) { +fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when (this) { is StructureShape -> { // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): // The only reason why the functions in this file have @@ -63,7 +63,7 @@ fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTy else -> false } -fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model: Model) = +fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled(model: Model): Boolean = hasPublicConstrainedWrapperTupleType(model, true) /** @@ -72,7 +72,7 @@ fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled( * This function is used in core code generators, so it takes in a [CoreCodegenContext] that is downcast * to [ServerCodegenContext] when generating servers. */ -fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, coreCodegenContext: CoreCodegenContext) = +fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, coreCodegenContext: CoreCodegenContext): Boolean = coreCodegenContext.target == CodegenTarget.SERVER && shape.hasPublicConstrainedWrapperTupleType( coreCodegenContext.model, @@ -106,13 +106,13 @@ fun Shape.typeNameContainsNonPublicType( fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) -fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider) = +fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider): Boolean = this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) -fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider) = +fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider): Boolean = !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) -fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider) = +fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = if (this is MemberShape) { // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, From 5396e734ac4f8ef321aae5cb2246c0d42339deab Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:03:50 +0200 Subject: [PATCH 204/255] Adjust docs for PubCrateConstrainedShapeSymbolProvider https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977727214 --- .../client/smithy/PubCrateConstrainedShapeSymbolProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 18caf5d5da..92d89cc8e4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -37,7 +37,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * have associated types that are generated as `pub(crate)`. See the * `PubCrate*Generator` classes to see how these types are generated. * - * It is important that this symbol provider _not_ wrap + * It is important that this symbol provider does _not_ wrap * [ConstrainedShapeSymbolProvider], since otherwise it will eventually * delegate to it and generate a symbol with a `pub` type. * From 881ecb3f11fcdd4b5f9e09bdd02fff147476ebc5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:05:40 +0200 Subject: [PATCH 205/255] Adjust docs in PubCrateConstrainedShapeSymbolProvider https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977729714 --- .../client/smithy/PubCrateConstrainedShapeSymbolProvider.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 92d89cc8e4..270b87291f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -41,9 +41,9 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * [ConstrainedShapeSymbolProvider], since otherwise it will eventually * delegate to it and generate a symbol with a `pub` type. * - * Note simple shapes cannot be transitively but not directly constrained, so - * this symbol provider is only implemented for aggregate shapes. The symbol - * provider will intentionally crash in such a case to avoid the caller + * Note simple shapes cannot be transitively and not directly constrained at + * the same time, so this symbol provider is only implemented for aggregate shapes. + * The symbol provider will intentionally crash in such a case to avoid the caller * incorrectly using it. * * Note also that for the purposes of this symbol provider, a member shape is From ef4b7ba0041e4539e3b1e84302cf73123ace33f6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:08:15 +0200 Subject: [PATCH 206/255] Type `Boolean` as return type in functinos for StructureGenerator https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977754410 --- .../codegen/client/smithy/generators/StructureGenerator.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt index 6c1b7ee10f..c289d21c42 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/StructureGenerator.kt @@ -114,7 +114,7 @@ open class StructureGenerator( model: Model, symbolProvider: SymbolProvider, takeInUnconstrainedTypes: Boolean, - ) = + ): Boolean = if (takeInUnconstrainedTypes) { structureShape.canReachConstrainedShape(model, symbolProvider) } else { @@ -133,7 +133,7 @@ open class StructureGenerator( fun serverHasFallibleBuilderWithoutPublicConstrainedTypes( structureShape: StructureShape, symbolProvider: SymbolProvider, - ) = + ): Boolean = structureShape .members() .map { symbolProvider.toSymbol(it) } From 48920b539f1f0c43f38d0b9b7a159017b9291c00 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:16:24 +0200 Subject: [PATCH 207/255] Try to make ErrorGenerator messageSymbol return type calculation a bit simpler https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977756365 --- .../smithy/generators/error/ErrorGenerator.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt index 05e7a03195..3dd8240708 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt @@ -85,24 +85,20 @@ class ErrorGenerator( } if (messageShape != null) { val messageSymbol = symbolProvider.toSymbol(messageShape).mapRustType { t -> t.asDeref() } - val (returnType, message) = if (messageSymbol.rustType() - .stripOuter() is RustType.Opaque - ) { + val messageType = messageSymbol.rustType() + val (returnType, message) = if (messageType.stripOuter() is RustType.Opaque) { // The string shape has a constraint trait that makes its symbol be a wrapper tuple struct. if (messageSymbol.isOptional()) { - "Option<&${ - messageSymbol.rustType().stripOuter().render() - }>" to "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" + "Option<&${messageType.stripOuter().render()}>" to + "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" } else { - "&${messageSymbol.rustType().render()}" to "&self.${symbolProvider.toMemberName(messageShape)}" + "&${messageType.render()}" to "&self.${symbolProvider.toMemberName(messageShape)}" } } else { if (messageSymbol.isOptional()) { - messageSymbol.rustType() - .render() to "self.${symbolProvider.toMemberName(messageShape)}.as_deref()" + messageType.render() to "self.${symbolProvider.toMemberName(messageShape)}.as_deref()" } else { - messageSymbol.rustType() - .render() to "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" + messageType.render() to "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" } } From 2c0c0d6dac09ad3ae367b89e761e104518a52929 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:20:45 +0200 Subject: [PATCH 208/255] Refactor `HttpBindingGenerator.renderPrefixHeader` https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977759613 --- .../client/smithy/generators/http/HttpBindingGenerator.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/HttpBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/HttpBindingGenerator.kt index b7da7ad67c..efbfa7ab5f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/HttpBindingGenerator.kt @@ -553,9 +553,14 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> val listHeader = memberType is CollectionShape + val iterableExpression = if (workingWithPublicConstrainedWrapperTupleType(memberShape, coreCodegenContext)) { + "&$field.0" + } else { + field + } rustTemplate( """ - for (k, v) in ${ if (workingWithPublicConstrainedWrapperTupleType(memberShape, coreCodegenContext)) "&$field.0" else field } { + for (k, v) in $iterableExpression { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} From 42ee464aee3170363cdc13b24101752970ba3bf9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:23:06 +0200 Subject: [PATCH 209/255] Document and rename to `AggregateShapeReachableFromOperationInputTagTrait` --- .../AggregateShapesReachableFromOperationInputTagger.kt | 6 +++--- ...> AggregateShapeReachableFromOperationInputTagTrait.kt} | 7 ++++++- .../amazon/smithy/rust/codegen/core/util/Smithy.kt | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) rename codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/{SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt => AggregateShapeReachableFromOperationInputTagTrait.kt} (62%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt index db9489ef30..99d276e8a5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -12,12 +12,12 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticAggregateShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE /** * Tag all [aggregate shapes] reachable from operation input with the - * [SyntheticAggregateShapeReachableFromOperationInputTagTrait] tag. + * [AggregateShapeReachableFromOperationInputTagTrait] tag. * * This is useful to determine whether we need to generate code to * enforce constraints upon request deserialization in the server. @@ -55,7 +55,7 @@ object AggregateShapesReachableFromOperationInputTagger { is MapShape -> shape.toBuilder() else -> UNREACHABLE("the `when` is exhaustive") } - builder.addTrait(SyntheticAggregateShapeReachableFromOperationInputTagTrait()).build() + builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() } else { shape } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt similarity index 62% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt rename to codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt index e5fad1491c..293a4a27b8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/SyntheticAggregateShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt @@ -9,7 +9,12 @@ import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.AnnotationTrait -class SyntheticAggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { +/** + * Tag to indicate that an aggregate shape is reachable from operation input. + * + * See the [AggregateShapesReachableFromOperationInputTagger] model transform for how it's used. + */ +class AggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { companion object { val ID = ShapeId.from("smithy.api.internal#syntheticStructureReachableFromOperationInputTag") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 2482431f4e..be97bd6c7e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait -import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticAggregateShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait @@ -71,7 +71,7 @@ fun Shape.hasEventStreamMember(model: Model): Boolean { private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { is StructureShape, is UnionShape, is ListShape, is MapShape -> { - shape.hasTrait() + shape.hasTrait() } else -> PANIC("this method does not support shape type ${shape.type}") } From 281e61f3acf20912eba0bdba3f017c28fcccd85e Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:34:01 +0200 Subject: [PATCH 210/255] Move `isShapeReachableFromOperationInput` into `AggregateShapeReachableFromOperationInputTagTrait` https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977766465 --- .../client/smithy/generators/Instantiator.kt | 2 +- ...hapeReachableFromOperationInputTagTrait.kt | 20 +++++++++++++++++++ .../smithy/rust/codegen/core/util/Smithy.kt | 11 ---------- .../server/smithy/ServerCodegenVisitor.kt | 2 +- .../generators/ServerBuilderGenerator.kt | 2 +- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/Instantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/Instantiator.kt index abe7e4e2d8..60557a2c0b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/Instantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/Instantiator.kt @@ -45,10 +45,10 @@ import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.isOptional import software.amazon.smithy.rust.codegen.client.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.letIf diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt index 293a4a27b8..d36a47d8b5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt @@ -6,8 +6,16 @@ package software.amazon.smithy.rust.codegen.core.smithy.traits import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.AnnotationTrait +import software.amazon.smithy.rust.codegen.core.util.PANIC +import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * Tag to indicate that an aggregate shape is reachable from operation input. @@ -19,3 +27,15 @@ class AggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, val ID = ShapeId.from("smithy.api.internal#syntheticStructureReachableFromOperationInputTag") } } + +private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { + is StructureShape, is UnionShape, is ListShape, is MapShape -> { + shape.hasTrait() + } else -> PANIC("this method does not support shape type ${shape.type}") +} + +fun StructureShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun CollectionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun UnionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) +fun MapShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) + diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index be97bd6c7e..1b5b37228b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -69,17 +69,6 @@ fun Shape.hasEventStreamMember(model: Model): Boolean { return members().any { it.isEventStream(model) } } -private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { - is StructureShape, is UnionShape, is ListShape, is MapShape -> { - shape.hasTrait() - } else -> PANIC("this method does not support shape type ${shape.type}") -} - -fun StructureShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) -fun CollectionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) -fun UnionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) -fun MapShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) - fun OperationShape.isInputEventStream(model: Model): Boolean { return input.map { id -> model.expectShape(id).hasEventStreamMember(model) }.orElse(false) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 6c00fc57c5..240922f5d5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -48,9 +48,9 @@ import software.amazon.smithy.rust.codegen.client.smithy.transformers.EventStrea import software.amazon.smithy.rust.codegen.client.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.client.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.CommandFailed import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.runCommand import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedMapGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedStringGenerator diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 023798f501..e425a00445 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -44,8 +44,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.rustType import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType From 973f44ac6a65013a7cbc1e06a6d1426c4f335be6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 14:36:16 +0200 Subject: [PATCH 211/255] Add phrase about Python having `publicConstrainedTypes` set to `false` https://github.com/awslabs/smithy-rs/pull/1342#discussion_r977768215 --- .../codegen/server/python/smithy/PythonServerCodegenVisitor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index b6a2ece58a..1bbf9d117d 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -64,7 +64,8 @@ class PythonServerCodegenVisitor( model = codegenDecorator.transformModel(service, baseModel) - // `publicConstrainedTypes` must always be `false` for the Python server. + // `publicConstrainedTypes` must always be `false` for the Python server, since Python generates its own + // wrapper newtypes. settings = settings.copy(codegenConfig = settings.codegenConfig.copy(publicConstrainedTypes = false)) fun baseSymbolProviderFactory( From 1de207c39039ae614cdb5f813ed913c29b824de2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 15:11:59 +0200 Subject: [PATCH 212/255] Try to move MaybeConstrained, Constrained into aws-smithy-http-server But it's not possible, because if we do, then we need both `RuntimeType`s to declare a dependency on `ServerCargoDependency.SmithyHttpServer`, but we can't do that in `RuntimeType.kt`, which is in `codegen-core`, since we can't make `codegen-core` take a dependency on `codegen-server`. But if we put them in `ServerRuntimeType`, we need to move the `data class MaybeConstrained` (which would move the `MaybeConstrained` `RuntimeType`) out of `RustTypes`, since `RustTypes` is in in `codegen-core`. But `RustTypes` is `sealed`, and we can't inherit from a sealed class outside the module where the class is defined. We'd have to make `RustTypes` `abstract`. But then `RustType.render` can't be exhaustive, and I don't know how to continue. --- .../rust/codegen/client/rustlang/RustTypes.kt | 9 +---- .../codegen/client/smithy/RuntimeTypes.kt | 3 -- .../codegen/client/smithy/SymbolVisitor.kt | 17 ---------- .../server/smithy/ServerCodegenVisitor.kt | 2 +- .../server/smithy/ServerRuntimeType.kt | 6 ++++ .../server/smithy/ServerSymbolProvider.kt | 34 +++++++++++++++++++ .../generators/ConstrainedStringGenerator.kt | 5 +-- .../ConstrainedTraitForEnumGenerator.kt | 11 +++--- .../generators/ServerBuilderGenerator.kt | 4 +-- .../UnconstrainedCollectionGenerator.kt | 2 +- .../generators/UnconstrainedMapGenerator.kt | 2 +- .../generators/UnconstrainedUnionGenerator.kt | 5 +-- .../src/constrained.rs | 0 .../aws-smithy-http-server/src/lib.rs | 2 ++ 14 files changed, 60 insertions(+), 42 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt rename rust-runtime/{inlineable => aws-smithy-http-server}/src/constrained.rs (100%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt index 134d44f3c8..f6642e8706 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt @@ -22,7 +22,7 @@ fun autoDeref(input: String) = if (input.startsWith("&")) { /** * A hierarchy of types handled by Smithy codegen */ -sealed class RustType { +abstract class RustType { // TODO(kotlin): when Kotlin supports, sealed interfaces, seal Container /** @@ -148,12 +148,6 @@ sealed class RustType { } } - data class MaybeConstrained(override val member: RustType) : RustType(), Container { - val runtimeType: RuntimeType = RuntimeType.MaybeConstrained() - override val name = runtimeType.name!! - override val namespace = runtimeType.namespace - } - data class Box(override val member: RustType) : RustType(), Container { override val name = "Box" override val namespace = "std::boxed" @@ -243,7 +237,6 @@ fun RustType.render(fullyQualified: Boolean = true): String { is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" is RustType.Opaque -> this.name - is RustType.MaybeConstrained -> "${this.name}<${this.member.render(fullyQualified)}>" } return "$namespace$base" } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt index 64187eb4d0..aef4027eaf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt @@ -252,9 +252,6 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n func, CargoDependency.SmithyProtocolTestHelpers(runtimeConfig), "aws_smithy_protocol_test", ) - fun ConstrainedTrait() = RuntimeType("Constrained", InlineDependency.constrained(), namespace = "crate::constrained") - fun MaybeConstrained() = RuntimeType("MaybeConstrained", InlineDependency.constrained(), namespace = "crate::constrained") - val http = CargoDependency.Http.asType() fun Http(path: String): RuntimeType = RuntimeType(name = path, dependency = CargoDependency.Http, namespace = "http") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt index a4d2489ec9..665e7aa2b0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt @@ -121,23 +121,6 @@ fun Symbol.makeRustBoxed(): Symbol = .build() } -/** - * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). - * - * This is idempotent and will have no change if the type is already `MaybeConstrained`. - */ -fun Symbol.makeMaybeConstrained(): Symbol = - if (this.rustType() is RustType.MaybeConstrained) { - this - } else { - val rustType = RustType.MaybeConstrained(this.rustType()) - Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() - } - /** * Map the [RustType] of a symbol with [f]. * diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 240922f5d5..68ced26063 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -358,7 +358,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> enumShapeGeneratorFactory(codegenContext, writer, shape).render() - ConstrainedTraitForEnumGenerator(model, codegenContext.symbolProvider, writer, shape).render() + ConstrainedTraitForEnumGenerator(model, codegenContext, writer, shape).render() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index b28671ab73..14aa5355a8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -21,6 +21,12 @@ object ServerRuntimeType { val Phantom = RuntimeType("PhantomData", dependency = null, namespace = "std::marker") val Cow = RuntimeType("Cow", dependency = null, namespace = "std::borrow") + fun ConstrainedTrait(runtimeConfig: RuntimeConfig) = + RuntimeType("Constrained", ServerCargoDependency.SmithyHttpServer(runtimeConfig), namespace = "${runtimeConfig.crateSrcPrefix}_http_server::constrained") + + fun MaybeConstrained(runtimeConfig: RuntimeConfig) = + RuntimeType("MaybeConstrained", ServerCargoDependency.SmithyHttpServer(runtimeConfig), namespace = "${runtimeConfig.crateSrcPrefix}_http_server::constrained") + fun Router(runtimeConfig: RuntimeConfig) = RuntimeType("Router", ServerCargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::routing") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt new file mode 100644 index 0000000000..0881dd9aec --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt @@ -0,0 +1,34 @@ +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.rust.codegen.client.rustlang.RustType +import software.amazon.smithy.rust.codegen.client.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.client.smithy.rustType + +// TODO(https://github.com/awslabs/smithy-rs/issues/1724): This file is a placeholder for when the server project adds +// its own bottom-most symbol provider. + +data class MaybeConstrained(override val member: RustType, val runtimeConfig: RuntimeConfig) : RustType(), RustType.Container { + private val runtimeType: RuntimeType = ServerRuntimeType.MaybeConstrained(runtimeConfig) + override val name = runtimeType.name!! + override val namespace = runtimeType.namespace +} + +/** + * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). + * + * This is idempotent and will have no change if the type is already `MaybeConstrained`. + */ +fun Symbol.makeMaybeConstrained(runtimeConfig: RuntimeConfig): Symbol = + if (this.rustType() is MaybeConstrained) { + this + } else { + val rustType = MaybeConstrained(this.rustType(), runtimeConfig) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 6885012f54..6aa714398e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -19,9 +19,10 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage /** @@ -142,7 +143,7 @@ class ConstrainedStringGenerator( } } """, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), "ConstraintViolation" to constraintViolation, "MaybeConstrained" to symbol.makeMaybeConstrained(), "Display" to RuntimeType.Display, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index ad5a8dda74..e991d4afa4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -11,9 +11,10 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * [ConstrainedTraitForEnumGenerator] generates code that implements the [RuntimeType.ConstrainedTrait] trait on an @@ -21,14 +22,14 @@ import software.amazon.smithy.rust.codegen.core.util.expectTrait */ class ConstrainedTraitForEnumGenerator( val model: Model, - val symbolProvider: RustSymbolProvider, + val codegenContext: ServerCodegenContext, val writer: RustWriter, val shape: StringShape, ) { fun render() { shape.expectTrait() - val symbol = symbolProvider.toSymbol(shape) + val symbol = codegenContext.symbolProvider.toSymbol(shape) val name = symbol.name val unconstrainedType = "String" @@ -44,7 +45,7 @@ class ConstrainedTraitForEnumGenerator( } } """, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), "MaybeConstrained" to symbol.makeMaybeConstrained(), ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index e425a00445..16fb735477 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -36,7 +36,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilde import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.client.smithy.isOptional import software.amazon.smithy.rust.codegen.client.smithy.isRustBoxed -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.client.smithy.makeOptional import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.mapRustType @@ -49,6 +48,7 @@ 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 import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a builder for the Rust type associated with the [StructureShape]. @@ -108,7 +108,7 @@ class ServerBuilderGenerator( "Structure" to structureSymbol, "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, - "MaybeConstrained" to RuntimeType.MaybeConstrained(), + "MaybeConstrained" to ServerRuntimeType.MaybeConstrained(runtimeConfig), ) fun render(writer: RustWriter) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 5dd59a44fe..cd51d66070 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -13,8 +13,8 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 652e34b11b..44a47f04a5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -16,8 +16,8 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 66531a3493..e0d6e5ed70 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -24,13 +24,14 @@ import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape 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.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ @@ -119,7 +120,7 @@ class UnconstrainedUnionGenerator( } } """, - "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), + "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "ConstrainedSymbol" to constrainedSymbol, "UnconstrainedSymbol" to symbol, diff --git a/rust-runtime/inlineable/src/constrained.rs b/rust-runtime/aws-smithy-http-server/src/constrained.rs similarity index 100% rename from rust-runtime/inlineable/src/constrained.rs rename to rust-runtime/aws-smithy-http-server/src/constrained.rs diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 842b5cfdfd..04a52c6cf2 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -11,6 +11,8 @@ pub(crate) mod macros; pub mod body; +#[doc(hidden)] +pub mod constrained; pub(crate) mod error; pub mod extension; #[doc(hidden)] From 7c029d44cde6679b2e771d0199ea34660f3b19b4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 15:17:20 +0200 Subject: [PATCH 213/255] Revert "Try to move MaybeConstrained, Constrained into aws-smithy-http-server" This reverts commit 1de207c39039ae614cdb5f813ed913c29b824de2. --- .../rust/codegen/client/rustlang/RustTypes.kt | 9 ++++- .../codegen/client/smithy/RuntimeTypes.kt | 3 ++ .../codegen/client/smithy/SymbolVisitor.kt | 17 ++++++++++ .../server/smithy/ServerCodegenVisitor.kt | 2 +- .../server/smithy/ServerRuntimeType.kt | 6 ---- .../server/smithy/ServerSymbolProvider.kt | 34 ------------------- .../generators/ConstrainedStringGenerator.kt | 5 ++- .../ConstrainedTraitForEnumGenerator.kt | 11 +++--- .../generators/ServerBuilderGenerator.kt | 4 +-- .../UnconstrainedCollectionGenerator.kt | 2 +- .../generators/UnconstrainedMapGenerator.kt | 2 +- .../generators/UnconstrainedUnionGenerator.kt | 5 ++- .../aws-smithy-http-server/src/lib.rs | 2 -- .../src/constrained.rs | 0 14 files changed, 42 insertions(+), 60 deletions(-) delete mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt rename rust-runtime/{aws-smithy-http-server => inlineable}/src/constrained.rs (100%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt index f6642e8706..134d44f3c8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/rustlang/RustTypes.kt @@ -22,7 +22,7 @@ fun autoDeref(input: String) = if (input.startsWith("&")) { /** * A hierarchy of types handled by Smithy codegen */ -abstract class RustType { +sealed class RustType { // TODO(kotlin): when Kotlin supports, sealed interfaces, seal Container /** @@ -148,6 +148,12 @@ abstract class RustType { } } + data class MaybeConstrained(override val member: RustType) : RustType(), Container { + val runtimeType: RuntimeType = RuntimeType.MaybeConstrained() + override val name = runtimeType.name!! + override val namespace = runtimeType.namespace + } + data class Box(override val member: RustType) : RustType(), Container { override val name = "Box" override val namespace = "std::boxed" @@ -237,6 +243,7 @@ fun RustType.render(fullyQualified: Boolean = true): String { is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" is RustType.Opaque -> this.name + is RustType.MaybeConstrained -> "${this.name}<${this.member.render(fullyQualified)}>" } return "$namespace$base" } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt index aef4027eaf..64187eb4d0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RuntimeTypes.kt @@ -252,6 +252,9 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n func, CargoDependency.SmithyProtocolTestHelpers(runtimeConfig), "aws_smithy_protocol_test", ) + fun ConstrainedTrait() = RuntimeType("Constrained", InlineDependency.constrained(), namespace = "crate::constrained") + fun MaybeConstrained() = RuntimeType("MaybeConstrained", InlineDependency.constrained(), namespace = "crate::constrained") + val http = CargoDependency.Http.asType() fun Http(path: String): RuntimeType = RuntimeType(name = path, dependency = CargoDependency.Http, namespace = "http") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt index 665e7aa2b0..a4d2489ec9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/SymbolVisitor.kt @@ -121,6 +121,23 @@ fun Symbol.makeRustBoxed(): Symbol = .build() } +/** + * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). + * + * This is idempotent and will have no change if the type is already `MaybeConstrained`. + */ +fun Symbol.makeMaybeConstrained(): Symbol = + if (this.rustType() is RustType.MaybeConstrained) { + this + } else { + val rustType = RustType.MaybeConstrained(this.rustType()) + Symbol.builder() + .rustType(rustType) + .addReference(this) + .name(rustType.name) + .build() + } + /** * Map the [RustType] of a symbol with [f]. * diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 68ced26063..240922f5d5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -358,7 +358,7 @@ open class ServerCodegenVisitor( logger.info("[rust-server-codegen] Generating an enum $shape") rustCrate.useShapeWriter(shape) { writer -> enumShapeGeneratorFactory(codegenContext, writer, shape).render() - ConstrainedTraitForEnumGenerator(model, codegenContext, writer, shape).render() + ConstrainedTraitForEnumGenerator(model, codegenContext.symbolProvider, writer, shape).render() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index 14aa5355a8..b28671ab73 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -21,12 +21,6 @@ object ServerRuntimeType { val Phantom = RuntimeType("PhantomData", dependency = null, namespace = "std::marker") val Cow = RuntimeType("Cow", dependency = null, namespace = "std::borrow") - fun ConstrainedTrait(runtimeConfig: RuntimeConfig) = - RuntimeType("Constrained", ServerCargoDependency.SmithyHttpServer(runtimeConfig), namespace = "${runtimeConfig.crateSrcPrefix}_http_server::constrained") - - fun MaybeConstrained(runtimeConfig: RuntimeConfig) = - RuntimeType("MaybeConstrained", ServerCargoDependency.SmithyHttpServer(runtimeConfig), namespace = "${runtimeConfig.crateSrcPrefix}_http_server::constrained") - fun Router(runtimeConfig: RuntimeConfig) = RuntimeType("Router", ServerCargoDependency.SmithyHttpServer(runtimeConfig), "${runtimeConfig.crateSrcPrefix}_http_server::routing") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt deleted file mode 100644 index 0881dd9aec..0000000000 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProvider.kt +++ /dev/null @@ -1,34 +0,0 @@ -package software.amazon.smithy.rust.codegen.server.smithy - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.rustType - -// TODO(https://github.com/awslabs/smithy-rs/issues/1724): This file is a placeholder for when the server project adds -// its own bottom-most symbol provider. - -data class MaybeConstrained(override val member: RustType, val runtimeConfig: RuntimeConfig) : RustType(), RustType.Container { - private val runtimeType: RuntimeType = ServerRuntimeType.MaybeConstrained(runtimeConfig) - override val name = runtimeType.name!! - override val namespace = runtimeType.namespace -} - -/** - * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained`). - * - * This is idempotent and will have no change if the type is already `MaybeConstrained`. - */ -fun Symbol.makeMaybeConstrained(runtimeConfig: RuntimeConfig): Symbol = - if (this.rustType() is MaybeConstrained) { - this - } else { - val rustType = MaybeConstrained(this.rustType(), runtimeConfig) - Symbol.builder() - .rustType(rustType) - .addReference(this) - .name(rustType.name) - .build() - } - diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 6aa714398e..6885012f54 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -19,10 +19,9 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage /** @@ -143,7 +142,7 @@ class ConstrainedStringGenerator( } } """, - "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "ConstraintViolation" to constraintViolation, "MaybeConstrained" to symbol.makeMaybeConstrained(), "Display" to RuntimeType.Display, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index e991d4afa4..ad5a8dda74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -11,10 +11,9 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * [ConstrainedTraitForEnumGenerator] generates code that implements the [RuntimeType.ConstrainedTrait] trait on an @@ -22,14 +21,14 @@ import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained */ class ConstrainedTraitForEnumGenerator( val model: Model, - val codegenContext: ServerCodegenContext, + val symbolProvider: RustSymbolProvider, val writer: RustWriter, val shape: StringShape, ) { fun render() { shape.expectTrait() - val symbol = codegenContext.symbolProvider.toSymbol(shape) + val symbol = symbolProvider.toSymbol(shape) val name = symbol.name val unconstrainedType = "String" @@ -45,7 +44,7 @@ class ConstrainedTraitForEnumGenerator( } } """, - "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "MaybeConstrained" to symbol.makeMaybeConstrained(), ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 16fb735477..e425a00445 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -36,6 +36,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilde import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.client.smithy.isOptional import software.amazon.smithy.rust.codegen.client.smithy.isRustBoxed +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.client.smithy.makeOptional import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.mapRustType @@ -48,7 +49,6 @@ 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 import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a builder for the Rust type associated with the [StructureShape]. @@ -108,7 +108,7 @@ class ServerBuilderGenerator( "Structure" to structureSymbol, "From" to RuntimeType.From, "TryFrom" to RuntimeType.TryFrom, - "MaybeConstrained" to ServerRuntimeType.MaybeConstrained(runtimeConfig), + "MaybeConstrained" to RuntimeType.MaybeConstrained(), ) fun render(writer: RustWriter) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index cd51d66070..5dd59a44fe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -13,8 +13,8 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 44a47f04a5..652e34b11b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -16,8 +16,8 @@ import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index e0d6e5ed70..66531a3493 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -24,14 +24,13 @@ import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape 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.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.makeMaybeConstrained /** * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ @@ -120,7 +119,7 @@ class UnconstrainedUnionGenerator( } } """, - "ConstrainedTrait" to ServerRuntimeType.ConstrainedTrait(codegenContext.runtimeConfig), + "ConstrainedTrait" to RuntimeType.ConstrainedTrait(), "MaybeConstrained" to constrainedSymbol.makeMaybeConstrained(), "ConstrainedSymbol" to constrainedSymbol, "UnconstrainedSymbol" to symbol, diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 04a52c6cf2..842b5cfdfd 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -11,8 +11,6 @@ pub(crate) mod macros; pub mod body; -#[doc(hidden)] -pub mod constrained; pub(crate) mod error; pub mod extension; #[doc(hidden)] diff --git a/rust-runtime/aws-smithy-http-server/src/constrained.rs b/rust-runtime/inlineable/src/constrained.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/constrained.rs rename to rust-runtime/inlineable/src/constrained.rs From a180566c917006b1e2e5ac8e54338415855676e7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 16:00:03 +0200 Subject: [PATCH 214/255] Make PubCrateConstraintViolationSymbolProvider wrap ConstraintViolationSymbolProvider https://github.com/awslabs/smithy-rs/pull/1342#discussion_r982907329 --- .../server/smithy/PubCrateConstraintViolationSymbolProvider.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt index 2ce6236214..369467f6a8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.client.rustlang.RustType +import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.rustType @@ -18,7 +19,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.rustType * This must wrap [ConstraintViolationSymbolProvider]. */ class PubCrateConstraintViolationSymbolProvider( - private val base: RustSymbolProvider, + private val base: ConstraintViolationSymbolProvider, ) : WrappingSymbolProvider(base) { override fun toSymbol(shape: Shape): Symbol { val baseSymbol = base.toSymbol(shape) From af57b95655889884b6aeeed53c4eb398e83ba757 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 3 Oct 2022 16:08:47 +0200 Subject: [PATCH 215/255] Constraint violation error messages start with uppercase https://github.com/awslabs/smithy-rs/pull/1342#discussion_r983608415 --- .../smithy/generators/ServerBuilderConstraintViolations.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index a1fe4582c7..620604f15f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -111,7 +111,7 @@ class ServerBuilderConstraintViolations( for (constraintViolation in all) { when (constraintViolation.kind) { ConstraintViolationKind.MISSING_MEMBER -> { - writer.docs("${constraintViolation.message(symbolProvider, model).replaceFirstChar { it.uppercase() }}.") + writer.docs("${constraintViolation.message(symbolProvider, model).replaceFirstChar { it.uppercaseChar() }}.") writer.rust("${constraintViolation.name()},") } @@ -127,7 +127,7 @@ class ServerBuilderConstraintViolations( // Note we cannot express the inner constraint violation as `>::Error`, because `T` might // be `pub(crate)` and that would leak `T` in a public interface. - writer.docs("${constraintViolation.message(symbolProvider, model)}.") + writer.docs("${constraintViolation.message(symbolProvider, model)}.".replaceFirstChar { it.uppercaseChar() }) Attribute.DocHidden.render(writer) writer.rust("${constraintViolation.name()}(#T),", constraintViolationSymbol) } From 7b1a2a922e9f18e95f0c73949d197607cc427dde Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Oct 2022 09:59:54 +0200 Subject: [PATCH 216/255] save work --- .../core/smithy/protocols/parse/JsonParserGenerator.kt | 4 ++++ .../codegen/server/smithy/protocols/ServerAwsJson.kt | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 907473f3a8..ebb2429f1d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -581,6 +581,10 @@ class JsonParserGenerator( } } + // TODO These two functions should be taken in as input to `JsonParserGenerator`. Taking in functions is ok, since + // we already take in `::awsJsonFieldName`. Then the `ServerProtocol`s can pass in functions that use `ServerCodegenContext`. + // Same goes with `builderSymbol`. + /** * Whether we should parse a value for a shape into its associated unconstrained type. For example, when the shape * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index c875596982..e82cea742a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols -import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -13,7 +12,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport -import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory @@ -94,11 +92,3 @@ class ServerAwsJsonSerializerGenerator( customizations = listOf(ServerAwsJsonError(awsJsonVersion)), ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator - -class ServerAwsJson( - codegenContext: CodegenContext, - awsJsonVersion: AwsJsonVersion, -) : AwsJson(codegenContext, awsJsonVersion) { - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = - ServerAwsJsonSerializerGenerator(codegenContext, httpBindingResolver, awsJsonVersion) -} From 62b279c383cfd19ade7f9b7c1cba26a626683b2d Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Oct 2022 11:06:41 +0200 Subject: [PATCH 217/255] save work --- .../smithy/generators/BuilderGenerator.kt | 18 ------ .../codegen/core/smithy/protocols/AwsJson.kt | 13 +++- .../codegen/core/smithy/protocols/RestJson.kt | 12 +++- .../protocols/parse/JsonParserGenerator.kt | 41 +++++-------- .../generators/ServerBuilderGenerator.kt | 61 +++++++++---------- .../smithy/generators/ServerBuilderSymbol.kt | 26 ++++++++ .../generators/protocol/ServerProtocol.kt | 28 ++++++++- .../ServerHttpBoundProtocolGenerator.kt | 11 +--- 8 files changed, 121 insertions(+), 89 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 750e04c5ee..48fed76ef0 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -48,24 +48,6 @@ fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { .build() } -// TODO Move this to `core`. -fun StructureShape.serverBuilderSymbol(symbolProvider: RustSymbolProvider, pubCrate: Boolean): Symbol { - val structureSymbol = symbolProvider.toSymbol(this) - val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + - if (pubCrate) { - "_internal" - } else { - "" - } - val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") - return Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .namespace(rustType.namespace, "::") - .definitionFile(structureSymbol.definitionFile) - .build() -} - fun RuntimeConfig.operationBuildError() = RuntimeType.operationModule(this).member("BuildError") fun RuntimeConfig.serializationError() = RuntimeType.operationModule(this).member("SerializationError") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 8c9588ada5..4d76112944 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -5,10 +5,13 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait @@ -19,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.serializationError import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator @@ -129,8 +133,13 @@ open class AwsJson( override fun additionalRequestHeaders(operationShape: OperationShape): List> = listOf("x-amz-target" to "${codegenContext.serviceShape.id.name}.${operationShape.id.name}") - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName) + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.builderSymbol(codegenContext.symbolProvider) + fun returnSymbolToParse(shape: Shape): Pair = + false to codegenContext.symbolProvider.toSymbol(shape) + return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsJsonSerializerGenerator(codegenContext, httpBindingResolver) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 67edb3b7fa..a7cb10b5d5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -5,9 +5,11 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.JsonNameTrait @@ -20,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator @@ -85,8 +88,13 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { override fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = listOf("x-amzn-errortype" to errorShape.id.name) - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.builderSymbol(codegenContext.symbolProvider) + fun returnSymbolToParse(shape: Shape): Pair = + false to codegenContext.symbolProvider.toSymbol(shape) + return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index ebb2429f1d..9484fe452c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -42,7 +41,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional @@ -58,10 +56,25 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.utils.StringUtils class JsonParserGenerator( - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, + /** Function that maps a StructureShape into its builder symbol */ + private val builderSymbol: (StructureShape) -> Symbol, + /** + * Whether we should parse a value for a shape into its associated unconstrained type. For example, when the shape + * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the + * user gets. This is only relevant for the server, that parses the incoming request and only after enforces + * constraint traits. + * + * The function returns a pair where the second component is the return symbol that should be parsed, and the first + * component is whether such symbol is unconstrained or not. + * + * TODO Try to store whether a symbol is unconstrained or not as a property on the `Symbol` itself, and so then + * this function should just need to return a `Symbol` as opposed to a pair. + */ + private val returnSymbolToParse: (Shape) -> Pair, ) : StructuredDataParserGenerator { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -580,26 +593,4 @@ class JsonParserGenerator( } } } - - // TODO These two functions should be taken in as input to `JsonParserGenerator`. Taking in functions is ok, since - // we already take in `::awsJsonFieldName`. Then the `ServerProtocol`s can pass in functions that use `ServerCodegenContext`. - // Same goes with `builderSymbol`. - - /** - * Whether we should parse a value for a shape into its associated unconstrained type. For example, when the shape - * is a `StructureShape`, we should construct and return a builder instead of building into the final `struct` the - * user gets. This is only relevant for the server, that parses the incoming request and only after enforces - * constraint traits. - * - * The function returns a pair where the second component is the return symbol that should be parsed, and the first - * component is whether such symbol is unconstrained or not. - */ - private fun returnSymbolToParse(shape: Shape): Pair = - if (codegenTarget == CodegenTarget.SERVER && shape.canReachConstrainedShape(model, symbolProvider)) { - true to (codegenContext as ServerCodegenContext).unconstrainedShapeSymbolProvider.toSymbol(shape) - } else { - false to symbolProvider.toSymbol(shape) - } - - private fun builderSymbol(shape: StructureShape) = shape.builderSymbol(codegenContext, symbolProvider) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index e425a00445..ccff069c34 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,45 +9,44 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.conditionalBlock -import software.amazon.smithy.rust.codegen.client.rustlang.deprecatedShape -import software.amazon.smithy.rust.codegen.client.rustlang.docs -import software.amazon.smithy.rust.codegen.client.rustlang.documentShape -import software.amazon.smithy.rust.codegen.client.rustlang.implInto -import software.amazon.smithy.rust.codegen.client.rustlang.render -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlockTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.stripOuter -import software.amazon.smithy.rust.codegen.client.rustlang.withBlock -import software.amazon.smithy.rust.codegen.client.rustlang.writable import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.client.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.client.smithy.isOptional -import software.amazon.smithy.rust.codegen.client.smithy.isRustBoxed -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeOptional -import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed -import software.amazon.smithy.rust.codegen.client.smithy.mapRustType -import software.amazon.smithy.rust.codegen.client.smithy.rustType import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.documentShape +import software.amazon.smithy.rust.codegen.core.rustlang.implInto +import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.makeOptional +import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.mapRustType +import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput 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 +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType /** @@ -96,7 +95,7 @@ class ServerBuilderGenerator( private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) + private val builderSymbol = shape.serverBuilderSymbol(codegenContext) private val moduleName = builderSymbol.namespace.split(builderSymbol.namespaceDelimiter).last() private val isBuilderFallible = StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt new file mode 100644 index 0000000000..9e8a61674d --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt @@ -0,0 +1,26 @@ +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext + +fun StructureShape.serverBuilderSymbol(codegenContext: ServerCodegenContext): Symbol { + val structureSymbol = codegenContext.symbolProvider.toSymbol(this) + val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + + if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { + "_internal" + } else { + "" + } + val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(structureSymbol.definitionFile) + .build() +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 651a47ac93..02f93d2205 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -5,8 +5,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -21,9 +25,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml +import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonSerializerGenerator private fun allOperations(codegenContext: CodegenContext): List { @@ -79,9 +88,9 @@ interface ServerProtocol : Protocol { } class ServerAwsJsonProtocol( - codegenContext: CodegenContext, + private val serverCodegenContext: ServerCodegenContext, awsJsonVersion: AwsJsonVersion, -) : AwsJson(codegenContext, awsJsonVersion), ServerProtocol { +) : AwsJson(serverCodegenContext, awsJsonVersion), ServerProtocol { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( "SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(runtimeConfig).asType(), @@ -89,11 +98,24 @@ class ServerAwsJsonProtocol( private val symbolProvider = codegenContext.symbolProvider private val service = codegenContext.serviceShape + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.serverBuilderSymbol(serverCodegenContext) + fun returnSymbolToParse(shape: Shape): Pair = + if (shape.canReachConstrainedShape(codegenContext.model, symbolProvider)) { + true to serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + false to codegenContext.symbolProvider.toSymbol(shape) + } + return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + } + override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = ServerAwsJsonSerializerGenerator(codegenContext, httpBindingResolver, awsJsonVersion) companion object { - fun fromCoreProtocol(awsJson: AwsJson): ServerAwsJsonProtocol = ServerAwsJsonProtocol(awsJson.codegenContext, awsJson.version) + fun fromCoreProtocol(awsJson: AwsJson): ServerAwsJsonProtocol = + ServerAwsJsonProtocol(awsJson.codegenContext as ServerCodegenContext, awsJson.version) } override fun markerStruct(): RuntimeType { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 24ee7f13d5..76e7afba78 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.model.traits.HttpErrorTrait import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.MediaTypeTrait +import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -33,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock -import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -44,10 +44,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.extractSymbolFromOption import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.MakeOperationGenerator @@ -58,7 +56,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDesc import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator -import software.amazon.smithy.rust.codegen.core.smithy.toOptional import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.smithy.wrapOptional @@ -80,6 +77,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerR import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import java.util.logging.Logger /** @@ -748,10 +746,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( Attribute.AllowUnusedMut.render(this) rust( "let mut input = #T::default();", - inputShape.serverBuilderSymbol( - symbolProvider, - !codegenContext.settings.codegenConfig.publicConstrainedTypes, - ), + inputShape.serverBuilderSymbol(codegenContext), ) val parser = structuredDataParser.serverInputParser(operationShape) val noInputs = model.expectShape(operationShape.inputShape).expectTrait().originalId == null From cadf33f56c45e87d9fa88515c73994ffc6b444c5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Oct 2022 14:13:07 +0200 Subject: [PATCH 218/255] fix merge at the expense of hardcoding always server + publicConstrainedTypes = true --- sst constraints test + cst works again --- .../ConstraintViolationSymbolProvider.kt | 12 +++- .../PubCrateConstrainedShapeSymbolProvider.kt | 10 ++- .../common-test-models/constraints.smithy | 72 +++++++++---------- .../rust/codegen/core/smithy/Constraints.kt | 15 ++-- ...BuilderSymbolButThisShouldNotStayInCore.kt | 27 +++++++ .../smithy/generators/StructureGenerator.kt | 18 +++-- .../generators/http/HttpBindingGenerator.kt | 3 +- .../http/RequestBindingGenerator.kt | 4 +- .../http/ResponseBindingGenerator.kt | 2 +- .../serialize/JsonSerializerGenerator.kt | 4 +- .../smithy/ConstrainedShapeSymbolProvider.kt | 20 +++--- ...bCrateConstraintViolationSymbolProvider.kt | 7 +- .../server/smithy/ServerCodegenContext.kt | 2 + .../server/smithy/ServerCodegenVisitor.kt | 11 ++- .../server/smithy/ServerRustSettings.kt | 1 - .../server/smithy/ServerSymbolProviders.kt | 5 +- .../UnconstrainedShapeSymbolProvider.kt | 9 ++- ...alidateUnsupportedConstraintsAreNotUsed.kt | 1 - .../generators/ConstrainedMapGenerator.kt | 16 ++--- .../generators/ConstrainedStringGenerator.kt | 26 +++---- .../ConstrainedTraitForEnumGenerator.kt | 10 +-- .../MapConstraintViolationGenerator.kt | 28 ++++---- .../PubCrateConstrainedCollectionGenerator.kt | 12 ++-- .../PubCrateConstrainedMapGenerator.kt | 12 ++-- .../ServerBuilderConstraintViolations.kt | 24 +++---- ...rGeneratorWithoutPublicConstrainedTypes.kt | 34 ++++----- .../smithy/generators/ServerBuilderSymbol.kt | 23 +----- .../smithy/generators/ServerEnumGenerator.kt | 5 +- .../ServerStructureConstrainedTraitImpl.kt | 10 +-- .../UnconstrainedCollectionGenerator.kt | 14 ++-- .../generators/UnconstrainedMapGenerator.kt | 16 ++--- .../generators/UnconstrainedUnionGenerator.kt | 30 ++++---- .../http/ServerRequestBindingGenerator.kt | 6 +- .../http/ServerResponseBindingGenerator.kt | 6 +- .../generators/protocol/ServerProtocol.kt | 18 ++++- .../ServerHttpBoundProtocolGenerator.kt | 29 ++------ .../smithy/testutil/ServerTestHelpers.kt | 7 ++ 37 files changed, 299 insertions(+), 250 deletions(-) create mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt rename {codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/UnconstrainedShapeSymbolProvider.kt (95%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt index 240cce3b95..1b29ca7d80 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt @@ -14,9 +14,14 @@ 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.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustReservedWords -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.Models +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.contextName +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.toSnakeCase // TODO Move this file to `core` or `server`. @@ -88,6 +93,7 @@ class ConstraintViolationSymbolProvider( constraintViolationSymbolForCollectionOrMapOrUnionShape(shape) } is StructureShape -> { + // TODO This should work with serverBuilderSymbol val builderSymbol = shape.builderSymbol(base) val namespace = builderSymbol.namespace diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt index 270b87291f..dbc5ec2fac 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -16,8 +16,14 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.SimpleShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustReservedWords -import software.amazon.smithy.rust.codegen.client.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.Constrained +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality +import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing +import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index 66ef678bf1..f24d7f8ce0 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -11,25 +11,25 @@ use smithy.framework#ValidationException service ConstraintsService { operations: [ ConstrainedShapesOperation, -// ConstrainedHttpBoundShapesOperation, -// ConstrainedRecursiveShapesOperation, -// // `httpQueryParams` and `httpPrefixHeaders` are structurually -// // exclusive, so we need one operation per target shape type -// // combination. -// QueryParamsTargetingLengthMapOperation, -// QueryParamsTargetingMapOfLengthStringOperation, -// QueryParamsTargetingMapOfEnumStringOperation, -// QueryParamsTargetingMapOfListOfLengthStringOperation, -// QueryParamsTargetingMapOfSetOfLengthStringOperation, -// QueryParamsTargetingMapOfListOfEnumStringOperation, -// HttpPrefixHeadersTargetingLengthMapOperation, -// // TODO(https://github.com/awslabs/smithy-rs/issues/1431) -// // HttpPrefixHeadersTargetingMapOfEnumStringOperation, - -// NonStreamingBlobOperation, - -// StreamingBlobOperation, -// EventStreamsOperation, + ConstrainedHttpBoundShapesOperation, + ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfEnumStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, + QueryParamsTargetingMapOfListOfEnumStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + + NonStreamingBlobOperation, + + StreamingBlobOperation, + EventStreamsOperation, ], } @@ -253,33 +253,33 @@ blob StreamingBlob blob NonStreamingBlob structure ConA { -// @required -// conB: ConB, + @required + conB: ConB, -// optConB: ConB, + optConB: ConB, lengthString: LengthString, -// minLengthString: MinLengthString, -// maxLengthString: MaxLengthString, -// fixedLengthString: FixedLengthString, + minLengthString: MinLengthString, + maxLengthString: MaxLengthString, + fixedLengthString: FixedLengthString, -// conBList: ConBList, -// conBList2: ConBList2, + conBList: ConBList, + conBList2: ConBList2, -// conBSet: ConBSet, + conBSet: ConBSet, -// conBMap: ConBMap, + conBMap: ConBMap, -// mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, + mapOfMapOfListOfListOfConB: MapOfMapOfListOfListOfConB, -// constrainedUnion: ConstrainedUnion, -// enumString: EnumString, + constrainedUnion: ConstrainedUnion, + enumString: EnumString, -// listOfLengthString: ListOfLengthString, -// setOfLengthString: SetOfLengthString, -// mapOfLengthString: MapOfLengthString, + listOfLengthString: ListOfLengthString, + setOfLengthString: SetOfLengthString, + mapOfLengthString: MapOfLengthString, -// nonStreamingBlob: NonStreamingBlob + nonStreamingBlob: NonStreamingBlob } map MapOfLengthString { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt index 8bf6e3a4de..6c884353ab 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt @@ -71,15 +71,18 @@ fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled( /** * Helper function to determine whether a shape will map to a _public_ constrained wrapper tuple type. * - * This function is used in core code generators, so it takes in a [CoreCodegenContext] that is downcast + * This function is used in core code generators, so it takes in a [CodegenContext] that is downcast * to [ServerCodegenContext] when generating servers. */ fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, codegenContext: CodegenContext): Boolean = - codegenContext.target == CodegenTarget.SERVER && - shape.hasPublicConstrainedWrapperTupleType( - codegenContext.model, - (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes, - ) + // TODO We assume we're in the server and `publicConstrainedTypes` is `true` just so that it compiles and we can test + // , the correct implementation is commented. + shape.hasPublicConstrainedWrapperTupleType(codegenContext.model, true) +// codegenContext.target == CodegenTarget.SERVER && +// shape.hasPublicConstrainedWrapperTupleType( +// codegenContext.model, +// (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes, +// ) /** * Returns whether a shape's type _name_ contains a non-public type when `publicConstrainedTypes` is `false`. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt new file mode 100644 index 0000000000..b815296444 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt @@ -0,0 +1,27 @@ +package software.amazon.smithy.rust.codegen.core.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase + +// TODO Move this to `ServerBuilderSymbol` in codegen-server +fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: Boolean): Symbol { + val structureSymbol = symbolProvider.toSymbol(this) + val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + + if (pubCrate) { + "_internal" + } else { + "" + } + val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(structureSymbol.definitionFile) + .build() +} + diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 00ec98c53e..c00c123100 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -35,6 +35,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGen import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -61,13 +62,16 @@ fun StructureShape.builderSymbol( codegenContext: CodegenContext, symbolProvider: RustSymbolProvider, ) = - when (codegenContext.target) { - CodegenTarget.CLIENT -> this.builderSymbol(symbolProvider) - CodegenTarget.SERVER -> { - val publicConstrainedTypes = (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes - this.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) - } - } + // TODO We assume we're in the server and `publicConstrainedTypes` is `true` just so that it compiles and we can test, + // the correct implementation is commented. + this.serverBuilderSymbol(codegenContext.symbolProvider, false) +// when (codegenContext.target) { +// CodegenTarget.CLIENT -> this.builderSymbol(symbolProvider) +// CodegenTarget.SERVER -> { +// val publicConstrainedTypes = (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes +// this.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) +// } +// } open class StructureGenerator( val model: Model, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 924af56b5d..04f0cbf9e5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.http import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.shapes.BlobShape @@ -92,10 +93,10 @@ enum class HttpMessageType { class HttpBindingGenerator( private val protocol: Protocol, private val codegenContext: CodegenContext, + private val symbolProvider: SymbolProvider, private val operationShape: OperationShape, ) { private val runtimeConfig = codegenContext.runtimeConfig - private val symbolProvider = codegenContext.symbolProvider private val codegenTarget = codegenContext.target private val model = codegenContext.model private val service = codegenContext.serviceShape diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt index 9a55d74a48..ade83df6c8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt @@ -62,7 +62,7 @@ class RequestBindingGenerator( private val symbolProvider = codegenContext.symbolProvider private val runtimeConfig = codegenContext.runtimeConfig private val httpTrait = protocol.httpBindingResolver.httpTrait(operationShape) - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) private val index = HttpBindingIndex.of(model) private val Encoder = CargoDependency.SmithyTypes(runtimeConfig).asType().member("primitive::Encoder") @@ -99,7 +99,7 @@ class RequestBindingGenerator( rust( """ let builder = #{T}(input, builder)?; - """.trimIndent(), + """, addHeadersFn, ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt index 1de4cd2897..5ef7e6a960 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt @@ -17,7 +17,7 @@ class ResponseBindingGenerator( codegenContext: CodegenContext, operationShape: OperationShape, ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index fee691ed61..42de3ba6c6 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -185,7 +185,7 @@ class JsonSerializerGenerator( rust("let mut out = String::new();") rustTemplate("let mut object = #{JsonObjectWriter}::new(&mut out);", *codegenScope) serializeStructure(StructContext("object", "value", structureShape), includedMembers) - customizations.forEach { it.section(JsonSection.ServerError(structureShape, "object"))(this) } + customizations.forEach { customization -> customization.section(JsonSection.ServerError(structureShape, "object"))(this) } rust("object.finish();") rustTemplate("Ok(out)", *codegenScope) } @@ -351,6 +351,7 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMemberValue(context: MemberContext, target: Shape) { val writer = context.writerExpression + // TODO Use customization val value = if (workingWithPublicConstrainedWrapperTupleType(context.shape, codegenContext)) { ValueExpression.Value("${context.valueExpression.name}.0") } else { @@ -431,6 +432,7 @@ class JsonSerializerGenerator( val valueName = safeName("value") rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { val keyTarget = model.expectShape(context.shape.key.target) + // TODO Use customization. val keyExpression = if (workingWithPublicConstrainedWrapperTupleType(keyTarget, codegenContext)) { "$keyName.0.as_str()" } else if (keyTarget.hasTrait()) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 54a40368c1..67bd3fb63f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -15,17 +15,17 @@ 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.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.smithy.Models -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.contextName -import software.amazon.smithy.rust.codegen.client.smithy.handleOptionality -import software.amazon.smithy.rust.codegen.client.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.locatedIn -import software.amazon.smithy.rust.codegen.client.smithy.rustType -import software.amazon.smithy.rust.codegen.client.smithy.symbolBuilder +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.Models +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.contextName +import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality +import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing +import software.amazon.smithy.rust.codegen.core.smithy.locatedIn +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.toPascalCase diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt index 369467f6a8..f6526973cb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -7,11 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.rust.codegen.client.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.rustType /** * This is only used when `publicConstrainedTypes` is `false`. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt index a0ad38f04f..48a83a7af5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index cf2271961b..103e974043 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -23,18 +23,25 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.transformers.AggregateShapesReachableFromOperationInputTagger +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.Constrained import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.smithy.DefaultPublicModules +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt index 35dc657329..dbfc8356a2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustSettings.kt @@ -102,7 +102,6 @@ data class ServerCodegenConfig( ServerCodegenConfig( formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, debugMode = coreCodegenConfig.debugMode, - eventStreamAllowList = coreCodegenConfig.eventStreamAllowList, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt index f07535030f..cb9908a6f8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt @@ -9,9 +9,8 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.client.smithy.UnconstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig /** * Just a handy class to centralize initialization all the symbol providers required by the server code generators, to diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt similarity index 95% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index d39f6ae085..daec5b7317 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy +package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model @@ -16,16 +16,18 @@ 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.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.setDefault import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.core.util.toPascalCase @@ -72,8 +74,9 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * `codegen-server` subproject), because that symbol provider will return a * constrained type for shapes that have constraint traits attached. * - * TODO Move this to `core`; remove below sentence. + * TODO Move this to `server`; remove below sentence. * + * TODO This sentence below is not true now * While this symbol provider is only used by the server, it needs to be in the * `codegen` subproject because the (common to client and server) parsers use * it. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt index abfd575997..c44ba68468 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt @@ -17,7 +17,6 @@ import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.model.traits.UniqueItemsTrait -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.orNull diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index 0c67b153eb..bc4b2e53b2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -8,16 +8,16 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.documentShape -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.documentShape +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * [ConstrainedMapGenerator] generates a wrapper tuple newtype holding a constrained `std::collections::HashMap`. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 6885012f54..8fbd70cfcf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -7,21 +7,21 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.documentShape -import software.amazon.smithy.rust.codegen.client.rustlang.render -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.documentShape +import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index ad5a8dda74..288065d75c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -8,11 +8,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +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.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index a20624715b..953b2911e9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -8,13 +8,13 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider @@ -44,11 +44,15 @@ class MapConstraintViolationGenerator( val constraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(shape) val constraintViolationName = constraintViolationSymbol.name - val constraintViolationCodegenScope = arrayOf( - "KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape), - "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape), - "KeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape), - ) + val constraintViolationCodegenScopeMutableList: MutableList> = mutableListOf() + if (isKeyConstrained(keyShape, symbolProvider)) { + constraintViolationCodegenScopeMutableList.add("KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape)) + } + if (isValueConstrained(valueShape, model, symbolProvider)) { + constraintViolationCodegenScopeMutableList.add("ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape)) + constraintViolationCodegenScopeMutableList.add("KeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape)) + } + val constraintViolationCodegenScope = constraintViolationCodegenScopeMutableList.toTypedArray() val constraintViolationVisibility = if (publicConstrainedTypes) { Visibility.PUBLIC diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 2487c3e46d..8d25e88a83 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -7,12 +7,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index b84900549f..a3d9738616 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -8,12 +8,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 620604f15f..30388104a0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -4,19 +4,19 @@ 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.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.docs -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.writable -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.isOptional -import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait 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.toPascalCase diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 7ad8cc1542..311fde1dba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -8,23 +8,23 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.conditionalBlock -import software.amazon.smithy.rust.codegen.client.rustlang.deprecatedShape -import software.amazon.smithy.rust.codegen.client.rustlang.docs -import software.amazon.smithy.rust.codegen.client.rustlang.documentShape -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlockTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.withBlock -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.client.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilderSymbol -import software.amazon.smithy.rust.codegen.client.smithy.isOptional -import software.amazon.smithy.rust.codegen.client.smithy.makeOptional +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.documentShape +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.makeOptional +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt index 9e8a61674d..2ad90e4859 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt @@ -2,25 +2,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords -import software.amazon.smithy.rust.codegen.core.rustlang.RustType -import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -fun StructureShape.serverBuilderSymbol(codegenContext: ServerCodegenContext): Symbol { - val structureSymbol = codegenContext.symbolProvider.toSymbol(this) - val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + - if (!codegenContext.settings.codegenConfig.publicConstrainedTypes) { - "_internal" - } else { - "" - } - val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") - return Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .namespace(rustType.namespace, "::") - .definitionFile(structureSymbol.definitionFile) - .build() -} +fun StructureShape.serverBuilderSymbol(codegenContext: ServerCodegenContext): Symbol = + this.serverBuilderSymbol(codegenContext.symbolProvider, !codegenContext.settings.codegenConfig.publicConstrainedTypes) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 16e715a207..eea6793a70 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -5,19 +5,18 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext open class ServerEnumGenerator( val codegenContext: ServerCodegenContext, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt index 4341f0b791..8012d2b8be 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -6,11 +6,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +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.serverBuilderSymbol class ServerStructureConstrainedTraitImpl( private val symbolProvider: RustSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 5dd59a44fe..2baafe7400 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -6,14 +6,14 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 652e34b11b..9454b85617 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -7,16 +7,16 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 66531a3493..d841f459b5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -9,24 +9,24 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.rust.codegen.client.rustlang.Attribute -import software.amazon.smithy.rust.codegen.client.rustlang.RustMetadata -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.Visibility -import software.amazon.smithy.rust.codegen.client.rustlang.rust -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.withBlock -import software.amazon.smithy.rust.codegen.client.rustlang.withBlockTemplate -import software.amazon.smithy.rust.codegen.client.rustlang.writable -import software.amazon.smithy.rust.codegen.client.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.client.smithy.RustBoxTrait -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.client.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait 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.toPascalCase diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index c2f4c7e4b8..78fcbc0350 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -13,15 +13,15 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerRequestBindingGenerator( protocol: Protocol, - CodegenContext: CodegenContext, - unconstrainedShapeSymbolProvider: UnconstrainedShapeSymbolProvider, + codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { private val httpBindingGenerator = - HttpBindingGenerator(protocol, coreCodegenContext, unconstrainedShapeSymbolProvider, operationShape) + HttpBindingGenerator(protocol, codegenContext, codegenContext.unconstrainedShapeSymbolProvider, operationShape) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index 1967e4304c..f7528149f8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -12,13 +12,15 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerResponseBindingGenerator( protocol: Protocol, - codegenContext: CodegenContext, + codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, operationShape) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) fun generateAddHeadersFn(shape: Shape): RuntimeType? = httpBindingGenerator.generateAddHeadersFn(shape, HttpMessageType.RESPONSE) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 02f93d2205..f639d5d6da 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -225,12 +225,24 @@ private fun restRouterConstruction( } class ServerRestJsonProtocol( - codegenContext: CodegenContext, -) : RestJson(codegenContext), ServerProtocol { + private val serverCodegenContext: ServerCodegenContext, +) : RestJson(serverCodegenContext), ServerProtocol { val runtimeConfig = codegenContext.runtimeConfig + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.serverBuilderSymbol(serverCodegenContext) + fun returnSymbolToParse(shape: Shape): Pair = + if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { + true to serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape) + } else { + false to codegenContext.symbolProvider.toSymbol(shape) + } + return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + } + companion object { - fun fromCoreProtocol(restJson: RestJson): ServerRestJsonProtocol = ServerRestJsonProtocol(restJson.codegenContext) + fun fromCoreProtocol(restJson: RestJson): ServerRestJsonProtocol = ServerRestJsonProtocol(restJson.codegenContext as ServerCodegenContext) } override fun markerStruct() = ServerRuntimeType.Protocol("AwsRestJson1", "rest_json_1", runtimeConfig) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 76e7afba78..4a9e92fae5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -38,6 +38,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -52,6 +53,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.MakeO import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional +import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation @@ -535,7 +537,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( val bindings = httpBindingResolver.errorResponseBindings(it) - software.amazon.smithy.rust.codegen.core.rustlang.Attribute.AllowUnusedMut.render(this) + Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{http}::Response::builder();", *codegenScope) serverRenderResponseHeaders(operationShape, variantShape) @@ -571,7 +573,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( operationShape: OperationShape, bindings: List, ) { - software.amazon.smithy.rust.codegen.core.rustlang.Attribute.AllowUnusedMut.render(this) + Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{http}::Response::builder();", *codegenScope) serverRenderResponseHeaders(operationShape) bindings.find { it.location == HttpLocation.RESPONSE_CODE } @@ -736,12 +738,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( inputShape: StructureShape, bindings: List, ) { - val httpBindingGenerator = ServerRequestBindingGenerator( - protocol, - codegenContext, - unconstrainedShapeSymbolProvider, - operationShape, - ) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) val structuredDataParser = protocol.structuredDataParser(operationShape) Attribute.AllowUnusedMut.render(this) rust( @@ -1155,13 +1152,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverRenderHeaderParser(writer: RustWriter, binding: HttpBindingDescriptor, operationShape: OperationShape) { - val httpBindingGenerator = - ServerRequestBindingGenerator( - protocol, - codegenContext, - codegenContext.unconstrainedShapeSymbolProvider, - operationShape, - ) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) writer.rustTemplate( """ @@ -1175,13 +1166,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( private fun serverRenderPrefixHeadersParser(writer: RustWriter, binding: HttpBindingDescriptor, operationShape: OperationShape) { check(binding.location == HttpLocation.PREFIX_HEADERS) - val httpBindingGenerator = - ServerRequestBindingGenerator( - protocol, - codegenContext, - codegenContext.unconstrainedShapeSymbolProvider, - operationShape, - ) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) val deserializer = httpBindingGenerator.generateDeserializePrefixHeadersFn(binding) writer.rustTemplate( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 7e79aea192..8e03c342f6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -10,15 +10,22 @@ import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator // These are the settings we default to if the user does not override them in their `smithy-build.json`. val ServerTestSymbolVisitorConfig = SymbolVisitorConfig( From 94c3d6e86cb18fa45159c2e1fe8db2f82acd4395 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 5 Oct 2022 16:22:26 +0200 Subject: [PATCH 219/255] sst constraints,constraints_without_public_constrained_types test + ca work --- .../common-test-models/constraints.smithy | 40 +++++++++---------- .../rust/codegen/core/smithy/Constraints.kt | 12 ++---- .../smithy/generators/BuilderGenerator.kt | 2 +- .../smithy/generators/StructureGenerator.kt | 2 +- .../generators/http/HttpBindingGenerator.kt | 38 +++++++++++++----- .../http/RequestBindingGenerator.kt | 8 +++- .../http/ResponseBindingGenerator.kt | 11 ++++- .../codegen/core/smithy/protocols/AwsQuery.kt | 10 ++++- .../codegen/core/smithy/protocols/Ec2Query.kt | 10 ++++- .../codegen/core/smithy/protocols/RestXml.kt | 7 +++- .../parse/AwsQueryParserGenerator.kt | 4 ++ .../parse/Ec2QueryParserGenerator.kt | 4 ++ .../parse/EventStreamUnmarshallerGenerator.kt | 8 ++-- .../protocols/parse/RestXmlParserGenerator.kt | 4 ++ .../parse/XmlBindingTraitParserGenerator.kt | 6 +-- .../serialize/JsonSerializerGenerator.kt | 39 +++++++++--------- .../XmlBindingTraitSerializerGenerator.kt | 9 +---- .../MapConstraintViolationGenerator.kt | 2 +- .../ServerBuilderConstraintViolations.kt | 5 ++- .../generators/ServerBuilderGenerator.kt | 11 ++--- ...rGeneratorWithoutPublicConstrainedTypes.kt | 3 +- .../generators/UnconstrainedUnionGenerator.kt | 2 +- .../http/ServerRequestBindingGenerator.kt | 12 +++++- .../http/ServerResponseBindingGenerator.kt | 38 +++++++++++++++++- .../generators/protocol/ServerProtocol.kt | 7 +++- .../server/smithy/protocols/ServerAwsJson.kt | 5 ++- .../server/smithy/protocols/ServerRestJson.kt | 40 +++++++++++++++++++ 27 files changed, 239 insertions(+), 100 deletions(-) diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index f24d7f8ce0..e87e1fb1ae 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -10,26 +10,26 @@ use smithy.framework#ValidationException @title("ConstraintsService") service ConstraintsService { operations: [ - ConstrainedShapesOperation, - ConstrainedHttpBoundShapesOperation, - ConstrainedRecursiveShapesOperation, - // `httpQueryParams` and `httpPrefixHeaders` are structurually - // exclusive, so we need one operation per target shape type - // combination. - QueryParamsTargetingLengthMapOperation, - QueryParamsTargetingMapOfLengthStringOperation, - QueryParamsTargetingMapOfEnumStringOperation, - QueryParamsTargetingMapOfListOfLengthStringOperation, - QueryParamsTargetingMapOfSetOfLengthStringOperation, - QueryParamsTargetingMapOfListOfEnumStringOperation, - HttpPrefixHeadersTargetingLengthMapOperation, - // TODO(https://github.com/awslabs/smithy-rs/issues/1431) - // HttpPrefixHeadersTargetingMapOfEnumStringOperation, - - NonStreamingBlobOperation, - - StreamingBlobOperation, - EventStreamsOperation, + ConstrainedShapesOperation, + ConstrainedHttpBoundShapesOperation, + ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually + // exclusive, so we need one operation per target shape type + // combination. + QueryParamsTargetingLengthMapOperation, + QueryParamsTargetingMapOfLengthStringOperation, + QueryParamsTargetingMapOfEnumStringOperation, + QueryParamsTargetingMapOfListOfLengthStringOperation, + QueryParamsTargetingMapOfSetOfLengthStringOperation, + QueryParamsTargetingMapOfListOfEnumStringOperation, + HttpPrefixHeadersTargetingLengthMapOperation, + // TODO(https://github.com/awslabs/smithy-rs/issues/1431) + // HttpPrefixHeadersTargetingMapOfEnumStringOperation, + + NonStreamingBlobOperation, + + StreamingBlobOperation, + EventStreamsOperation, ], } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt index 6c884353ab..85b4c77185 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt @@ -74,15 +74,9 @@ fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled( * This function is used in core code generators, so it takes in a [CodegenContext] that is downcast * to [ServerCodegenContext] when generating servers. */ -fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, codegenContext: CodegenContext): Boolean = - // TODO We assume we're in the server and `publicConstrainedTypes` is `true` just so that it compiles and we can test - // , the correct implementation is commented. - shape.hasPublicConstrainedWrapperTupleType(codegenContext.model, true) -// codegenContext.target == CodegenTarget.SERVER && -// shape.hasPublicConstrainedWrapperTupleType( -// codegenContext.model, -// (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes, -// ) +// TODO Move this to the `codegen-server`. In fact, try to move everything to `codegen-server`. +fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, model: Model, publicConstrainedTypes: Boolean): Boolean = + shape.hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes) /** * Returns whether a shape's type _name_ contains a non-public type when `publicConstrainedTypes` is `false`. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 48fed76ef0..cd2369c5af 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -35,7 +35,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO Move this to `core`. +// TODO Move this to `client`. fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index c00c123100..945ad0b2e2 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -57,7 +57,7 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } } -// TODO Move this to `core`. +// TODO Delete this fun StructureShape.builderSymbol( codegenContext: CodegenContext, symbolProvider: RustSymbolProvider, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 04f0cbf9e5..315eba5c98 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.http import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex @@ -24,7 +25,6 @@ import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -42,6 +42,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -72,6 +74,17 @@ enum class HttpMessageType { REQUEST, RESPONSE } +/** + * Class describing an HTTP binding (de)serialization section that can be used in a customization. + */ +sealed class HttpBindingSection(name: String) : Section(name) { + // TODO `shape` should be `MapShape`. + data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: Shape) : + HttpBindingSection("BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders") +} + +typealias HttpBindingCustomization = NamedSectionGenerator + /** * This class generates Rust functions that (de)serialize data from/to an HTTP message. * They are useful for *both*: @@ -95,6 +108,9 @@ class HttpBindingGenerator( private val codegenContext: CodegenContext, private val symbolProvider: SymbolProvider, private val operationShape: OperationShape, + /** Function that maps a StructureShape into its builder symbol */ + private val builderSymbol: (StructureShape) -> Symbol, + private val customizations: List = listOf(), ) { private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target @@ -237,6 +253,7 @@ class HttpBindingGenerator( codegenContext, operationShape, targetShape, + builderSymbol, ).render() rustTemplate( """ @@ -548,15 +565,19 @@ class HttpBindingGenerator( else -> UNREACHABLE("unexpected member for prefix headers: $memberType") } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> + // TODO How can this be a collection shape? Makes no sense, `httpPrefixHeaders` is for map shapes. val listHeader = memberType is CollectionShape - val iterableExpression = if (workingWithPublicConstrainedWrapperTupleType(memberShape, codegenContext)) { - "&$field.0" - } else { - field + customizations.forEach { customization -> + customization.section( + HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders( + field, + memberType, + ), + )(this) } rustTemplate( """ - for (k, v) in $iterableExpression { + for (k, v) in $field { use std::str::FromStr; let header_name = http::header::HeaderName::from_str(&format!("{}{}", "${httpBinding.locationName}", &k)).map_err(|err| { #{build_error}::InvalidField { field: "$memberName", details: format!("`{}` cannot be used as a header name: {}", k, err)} @@ -597,10 +618,7 @@ class HttpBindingGenerator( val func = writer.format(RuntimeType.Base64Encode(runtimeConfig)) "$func(&$targetName)" } else { - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member traits are not - // supported yet. - val expr = if (workingWithPublicConstrainedWrapperTupleType(target, codegenContext)) "&$targetName.0" else targetName - quoteValue("AsRef::::as_ref($expr)") + quoteValue("$targetName.as_str()") } } target.isTimestampShape -> { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt index ade83df6c8..f1074c6cf4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.http +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.pattern.SmithyPattern @@ -12,6 +13,7 @@ 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.Shape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -24,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.generators.OperationBuildError +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -45,6 +48,7 @@ fun SmithyPattern.rustFormatString(prefix: String, separator: String): String { return base.dq() } +// TODO Move to `codegen-client` and update docs /** * Generates methods to serialize and deserialize requests based on the HTTP trait. Specifically: * 1. `fn update_http_request(builder: http::request::Builder) -> Builder` @@ -62,7 +66,9 @@ class RequestBindingGenerator( private val symbolProvider = codegenContext.symbolProvider private val runtimeConfig = codegenContext.runtimeConfig private val httpTrait = protocol.httpBindingResolver.httpTrait(operationShape) - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) + private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape, ::builderSymbol) private val index = HttpBindingIndex.of(model) private val Encoder = CargoDependency.SmithyTypes(runtimeConfig).asType().member("primitive::Encoder") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt index 5ef7e6a960..fbb961e545 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt @@ -5,19 +5,26 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.http +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +// TODO Move to `codegen-client` and update docs class ResponseBindingGenerator( protocol: Protocol, - codegenContext: CodegenContext, + private val codegenContext: CodegenContext, operationShape: OperationShape, ) { - private val httpBindingGenerator = HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) + private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) + + private val httpBindingGenerator = + HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape, ::builderSymbol) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt index d9d4224440..e97b243a14 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt @@ -6,9 +6,11 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait @@ -19,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.AwsQueryParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.AwsQuerySerializerGenerator @@ -55,8 +58,11 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - AwsQueryParserGenerator(codegenContext, awsQueryErrors) + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.builderSymbol(codegenContext.symbolProvider) + return AwsQueryParserGenerator(codegenContext, awsQueryErrors, ::builderSymbol) + } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsQuerySerializerGenerator(codegenContext) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt index 3500dfcab2..fdf5207e95 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt @@ -5,8 +5,10 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -16,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.Ec2QueryParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.Ec2QuerySerializerGenerator @@ -46,8 +49,11 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) + override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { + fun builderSymbol(shape: StructureShape): Symbol = + shape.builderSymbol(codegenContext.symbolProvider) + return Ec2QueryParserGenerator(codegenContext, ec2QueryErrors, ::builderSymbol) + } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = Ec2QuerySerializerGenerator(codegenContext) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt index b09092f467..f75b9cb2aa 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt @@ -6,7 +6,9 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols import software.amazon.smithy.aws.traits.protocols.RestXmlTrait +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -15,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.RestXmlParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator @@ -45,7 +48,9 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { TimestampFormatTrait.Format.DATE_TIME override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return RestXmlParserGenerator(codegenContext, restXmlErrors) + fun builderSymbol(shape: StructureShape): Symbol = + shape.builderSymbol(codegenContext.symbolProvider) + return RestXmlParserGenerator(codegenContext, restXmlErrors, ::builderSymbol) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt index cb0569c22d..a53bc2ab1b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGenerator.kt @@ -5,6 +5,8 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -27,10 +29,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType class AwsQueryParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, + builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, + builderSymbol, ) { context, inner -> val operationName = codegenContext.symbolProvider.toSymbol(context.shape).name val responseWrapperName = operationName + "Response" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt index c33e257377..f59f2df556 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGenerator.kt @@ -5,6 +5,8 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -25,10 +27,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType class Ec2QueryParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, + builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, + builderSymbol, ) { context, inner -> val operationName = codegenContext.symbolProvider.toSymbol(context.shape).name val responseWrapperName = operationName + "Response" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 0d15cc39f3..07c729b2f4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -52,6 +52,8 @@ class EventStreamUnmarshallerGenerator( private val codegenContext: CodegenContext, private val operationShape: OperationShape, private val unionShape: UnionShape, + /** Function that maps a StructureShape into its builder symbol */ + private val builderSymbol: (StructureShape) -> Symbol, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -193,7 +195,7 @@ class EventStreamUnmarshallerGenerator( ) } else -> { - rust("let mut builder = #T::default();", unionStruct.builderSymbol(codegenContext, symbolProvider)) + rust("let mut builder = #T::default();", builderSymbol(unionStruct)) val payloadMember = unionStruct.members().firstOrNull { it.hasTrait() } if (payloadMember != null) { renderUnmarshallEventPayload(payloadMember) @@ -335,7 +337,7 @@ class EventStreamUnmarshallerGenerator( val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) if (parser != null) { - rust("let mut builder = #T::default();", target.builderSymbol(codegenContext, symbolProvider)) + rust("let mut builder = #T::default();", builderSymbol(target)) rustTemplate( """ builder = #{parser}(&message.payload()[..], builder) @@ -358,7 +360,7 @@ class EventStreamUnmarshallerGenerator( val target = model.expectShape(member.target, StructureShape::class.java) val parser = protocol.structuredDataParser(operationShape).errorParser(target) val mut = if (parser != null) { " mut" } else { "" } - rust("let$mut builder = #T::default();", target.builderSymbol(codegenContext, symbolProvider)) + rust("let$mut builder = #T::default();", builderSymbol(target)) if (parser != null) { rustTemplate( """ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt index ed41cfd85a..156d025b9b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/RestXmlParserGenerator.kt @@ -5,6 +5,8 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -17,10 +19,12 @@ import software.amazon.smithy.rust.codegen.core.util.orNull class RestXmlParserGenerator( codegenContext: CodegenContext, xmlErrors: RuntimeType, + builderSymbol: (shape: StructureShape) -> Symbol, private val xmlBindingTraitParserGenerator: XmlBindingTraitParserGenerator = XmlBindingTraitParserGenerator( codegenContext, xmlErrors, + builderSymbol, ) { context, inner -> val shapeName = context.outputShapeName // Get the non-synthetic version of the outputShape and check to see if it has the `AllowInvalidXmlRoot` trait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 0e4c2c98a9..0c5f01c02b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import software.amazon.smithy.aws.traits.customizations.S3UnwrappedXmlOutputTrait import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.HttpBinding import software.amazon.smithy.model.knowledge.HttpBindingIndex @@ -68,8 +69,9 @@ data class OperationWrapperContext( ) class XmlBindingTraitParserGenerator( - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, private val xmlErrors: RuntimeType, + private val builderSymbol: (shape: StructureShape) -> Symbol, private val writeOperationWrapper: RustWriter.(OperationWrapperContext, OperationInnerWriteable) -> Unit, ) : StructuredDataParserGenerator { @@ -740,6 +742,4 @@ class XmlBindingTraitParserGenerator( private fun StructureShape.xmlMembers(): XmlMemberIndex { return XmlMemberIndex.fromMembers(this.members().toList()) } - - private fun builderSymbol(shape: StructureShape) = shape.builderSymbol(codegenContext, symbolProvider) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index 42de3ba6c6..91d83fcd94 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -19,9 +19,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.TimestampFormatTrait.Format.EPOCH_SECONDS -import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -49,7 +47,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape @@ -59,6 +56,8 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape sealed class JsonSection(name: String) : Section(name) { /** Mutate the server error object prior to finalization. Eg: this can be used to inject `__type` to record the error type. */ data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSection("ServerError") + // TODO This could take in directly the `Context` + data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSection("BeforeIteratingOverMap") } /** @@ -67,7 +66,7 @@ sealed class JsonSection(name: String) : Section(name) { typealias JsonCustomization = NamedSectionGenerator class JsonSerializerGenerator( - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, @@ -351,12 +350,14 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMemberValue(context: MemberContext, target: Shape) { val writer = context.writerExpression - // TODO Use customization - val value = if (workingWithPublicConstrainedWrapperTupleType(context.shape, codegenContext)) { - ValueExpression.Value("${context.valueExpression.name}.0") - } else { - context.valueExpression - } +// // TODO Use customization +// // let +// val value = if (workingWithPublicConstrainedWrapperTupleType(context.shape, codegenContext)) { +// ValueExpression.Value("${context.valueExpression.name}.0") +// } else { +// context.valueExpression +// } + val value = context.valueExpression when (target) { is StringShape -> rust("$writer.string(${value.name}.as_str());") @@ -430,16 +431,18 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMap(context: Context) { val keyName = safeName("key") val valueName = safeName("value") + customizations.forEach { customization -> customization.section(JsonSection.BeforeIteratingOverMap(context.shape, context.valueExpression))(this) } rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { val keyTarget = model.expectShape(context.shape.key.target) - // TODO Use customization. - val keyExpression = if (workingWithPublicConstrainedWrapperTupleType(keyTarget, codegenContext)) { - "$keyName.0.as_str()" - } else if (keyTarget.hasTrait()) { - "$keyName.as_str()" - } else { - keyName - } + // TODO Remove +// val keyExpression = if (workingWithPublicConstrainedWrapperTupleType(keyTarget, codegenContext)) { +// "$keyName.0.as_str()" +// } else if (keyTarget.hasTrait()) { +// "$keyName.as_str()" +// } else { +// keyName +// } + val keyExpression = "$keyName.as_str()" serializeMember(MemberContext.mapMember(context, keyExpression, valueName)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index c6b8013ce0..2a8ecc14d9 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.model.traits.XmlFlattenedTrait import software.amazon.smithy.model.traits.XmlNamespaceTrait -import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -56,7 +55,7 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.outputShape class XmlBindingTraitSerializerGenerator( - private val codegenContext: CodegenContext, + codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, ) : StructuredDataSerializerGenerator { private val symbolProvider = codegenContext.symbolProvider @@ -287,11 +286,7 @@ class XmlBindingTraitSerializerGenerator( private fun RustWriter.serializeRawMember(member: MemberShape, input: String) { when (model.expectShape(member.target)) { is StringShape -> { - if (workingWithPublicConstrainedWrapperTupleType(member, codegenContext)) { - rust("$input.0.as_str()") - } else { - rust("$input.as_str()") - } + rust("$input.as_str()") } is BooleanShape, is NumberShape -> { rust( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 953b2911e9..c92d18993a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -70,7 +70,7 @@ class MapConstraintViolationGenerator( rustTemplate( """ ##[derive(Debug, PartialEq)] - pub enum $constraintViolationName { + pub${ if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate) " else "" } enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} ${if (isKeyConstrained(keyShape, symbolProvider)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 30388104a0..845b701fd0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -7,6 +7,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -50,12 +51,12 @@ class ServerBuilderConstraintViolations( ) } - fun render(writer: RustWriter, nonExhaustive: Boolean) { + fun render(writer: RustWriter, visibility: Visibility, nonExhaustive: Boolean) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) writer.docs("Holds one variant for each of the ways the builder can fail.") if (nonExhaustive) Attribute.NonExhaustive.render(writer) val constraintViolationSymbolName = constraintViolationSymbolProvider.toSymbol(shape).name - writer.rustBlock("pub enum $constraintViolationSymbolName") { + writer.rustBlock("pub${ if (visibility == Visibility.PUBCRATE) " (crate) " else "" } enum $constraintViolationSymbolName") { renderConstraintViolations(writer) } renderImplDisplayConstraintViolation(writer) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ccff069c34..56ada0ef2d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -90,6 +90,7 @@ class ServerBuilderGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val visibility = if (publicConstrainedTypes) Visibility.PUBLIC else Visibility.PUBCRATE private val symbolProvider = codegenContext.symbolProvider private val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider private val pubCrateConstrainedShapeSymbolProvider = codegenContext.pubCrateConstrainedShapeSymbolProvider @@ -111,12 +112,6 @@ class ServerBuilderGenerator( ) fun render(writer: RustWriter) { - val visibility = if (publicConstrainedTypes) { - Visibility.PUBLIC - } else { - Visibility.PUBCRATE - } - writer.docs("See #D.", structureSymbol) writer.withModule(moduleName, RustMetadata(visibility = visibility)) { renderBuilder(this) @@ -125,7 +120,7 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - serverBuilderConstraintViolations.render(writer, nonExhaustive = true) + serverBuilderConstraintViolations.render(writer, visibility, nonExhaustive = true) // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is // an operation input shape. @@ -148,7 +143,7 @@ class ServerBuilderGenerator( val baseDerives = structureSymbol.expectRustMetadata().derives val derives = baseDerives.derives.intersect(setOf(RuntimeType.Debug, RuntimeType.Clone)) + RuntimeType.Default baseDerives.copy(derives = derives).render(writer) - writer.rustBlock("pub struct Builder") { + writer.rustBlock("pub${ if (visibility == Visibility.PUBCRATE) " (crate)" else "" } struct Builder") { members.forEach { renderBuilderMember(this, it) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 311fde1dba..0bd2f8cd03 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -73,7 +74,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - serverBuilderConstraintViolations.render(writer, nonExhaustive = false) + serverBuilderConstraintViolations.render(writer, Visibility.PUBLIC, nonExhaustive = false) renderTryFromBuilderImpl(writer) } else { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index d841f459b5..9af0173f8c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -135,7 +135,7 @@ class UnconstrainedUnionGenerator( RustMetadata(visibility = constraintViolationVisibility), ) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(this) - rustBlock("pub enum $constraintViolationName") { + rustBlock("pub${ if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate)" else "" } enum $constraintViolationName") { constraintViolations().forEach { renderConstraintViolation(this, it) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 78fcbc0350..6d4a5b28b0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -5,23 +5,31 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.http +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerRequestBindingGenerator( protocol: Protocol, - codegenContext: ServerCodegenContext, + private val codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { + private fun serverBuilderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol( + codegenContext.symbolProvider, + !codegenContext.settings.codegenConfig.publicConstrainedTypes, + ) private val httpBindingGenerator = - HttpBindingGenerator(protocol, codegenContext, codegenContext.unconstrainedShapeSymbolProvider, operationShape) + HttpBindingGenerator(protocol, codegenContext, codegenContext.unconstrainedShapeSymbolProvider, operationShape, ::serverBuilderSymbol) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index f7528149f8..b99e97a724 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -5,23 +5,57 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.http +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable 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.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerResponseBindingGenerator( protocol: Protocol, - codegenContext: ServerCodegenContext, + private val codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { + private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) + private val httpBindingGenerator = - HttpBindingGenerator(protocol, codegenContext, codegenContext.symbolProvider, operationShape) + HttpBindingGenerator( + protocol, + codegenContext, + codegenContext.symbolProvider, + operationShape, + ::builderSymbol, + listOf( + ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization( + codegenContext, + ), + ), + ) fun generateAddHeadersFn(shape: Shape): RuntimeType? = httpBindingGenerator.generateAddHeadersFn(shape, HttpMessageType.RESPONSE) } + +// TODO Docs +class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext): HttpBindingCustomization() { + override fun section(section: HttpBindingSection): Writable = when (section) { + is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders -> writable { + if (workingWithPublicConstrainedWrapperTupleType(section.shape, codegenContext.model, codegenContext.settings.codegenConfig.publicConstrainedTypes)) { + rust("let ${section.variableName} = &${section.variableName}.0;") + } + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index f639d5d6da..9c7ee9a943 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -28,12 +28,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonSerializerGenerator +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonSerializerGenerator private fun allOperations(codegenContext: CodegenContext): List { val index = TopDownIndex.of(codegenContext.model) @@ -111,7 +113,7 @@ class ServerAwsJsonProtocol( } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = - ServerAwsJsonSerializerGenerator(codegenContext, httpBindingResolver, awsJsonVersion) + ServerAwsJsonSerializerGenerator(serverCodegenContext, httpBindingResolver, awsJsonVersion) companion object { fun fromCoreProtocol(awsJson: AwsJson): ServerAwsJsonProtocol = @@ -241,6 +243,9 @@ class ServerRestJsonProtocol( return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) } + override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + ServerRestJsonSerializerGenerator(serverCodegenContext, httpBindingResolver) + companion object { fun fromCoreProtocol(restJson: RestJson): ServerRestJsonProtocol = ServerRestJsonProtocol(restJson.codegenContext as ServerCodegenContext) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index e82cea742a..f0abb2d076 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -71,6 +71,7 @@ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonCusto rust("""${section.jsonObject}.key("__type").string("${escape(typeId)}");""") } } + is JsonSection.BeforeIteratingOverMap -> emptySection } } @@ -81,7 +82,7 @@ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonCusto * https://awslabs.github.io/smithy/1.0/spec/aws/aws-json-1_0-protocol.html#operation-error-serialization */ class ServerAwsJsonSerializerGenerator( - private val codegenContext: CodegenContext, + private val codegenContext: ServerCodegenContext, private val httpBindingResolver: HttpBindingResolver, private val awsJsonVersion: AwsJsonVersion, private val jsonSerializerGenerator: JsonSerializerGenerator = @@ -89,6 +90,6 @@ class ServerAwsJsonSerializerGenerator( codegenContext, httpBindingResolver, ::awsJsonFieldName, - customizations = listOf(ServerAwsJsonError(awsJsonVersion)), + customizations = listOf(ServerAwsJsonError(awsJsonVersion), ServerBeforeIteratingOverMapCustomization(codegenContext)), ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index d3d0fea631..213044de84 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,9 +5,19 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols +import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport +import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol @@ -36,3 +46,33 @@ class ServerRestJsonFactory : ProtocolGeneratorFactory emptySection + is JsonSection.BeforeIteratingOverMap -> writable { + if (workingWithPublicConstrainedWrapperTupleType( + section.shape, + codegenContext.model, + codegenContext.settings.codegenConfig.publicConstrainedTypes, + ) + ) { + rust("""let ${section.valueExpression.name} = &${section.valueExpression.name}.0;""") + } + } + } +} From be594bbb0e51b240b74071e8a1dc8a28074a90ac Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 6 Oct 2022 14:45:52 +0200 Subject: [PATCH 220/255] attach ValidationException on operations whose input is constrained for services in CI --- ...ShapesReachableFromOperationInputTagger.kt | 2 +- ...ToConstrainedOperationInputsInAllowList.kt | 66 +++++++++++++++++++ .../smithy.framework.validation.smithy | 36 ++++++++++ .../rust/codegen/core/smithy/Constraints.kt | 16 +++++ codegen-server-test/build.gradle.kts | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 3 + .../generators/protocol/ServerProtocol.kt | 2 +- 7 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt create mode 100644 codegen-core/common-test-models/smithy.framework.validation.smithy diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt index 99d276e8a5..63985ace8e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -32,7 +32,7 @@ import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE * * [aggregate shapes]: https://awslabs.github.io/smithy/2.0/spec/aggregate-types.html#aggregate-types * - * TODO Move this to `core`, together with all the model transformers. + * TODO Move this to `server`. */ object AggregateShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt new file mode 100644 index 0000000000..6c3f10c621 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.EnumShape +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.SetShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.core.util.inputShape + +/** + * TODO Docs + * TODO Move to server + */ +object AttachValidationExceptionToConstrainedOperationInputsInAllowList { + // TODO Use fully qualified shapeIds + private val serviceShapeIdNameAllowList = setOf("RestJson") + + fun transform(model: Model): Model { +// model.serviceShapes.filter { it.toShapeId().name == "RestJsonValidation" }[0].operations.map { model.expectShape(it, OperationShape::class.java) }.map { it.errors.add( +// ShapeId.from("smithy.framework#ValidationException")) } + + val walker = Walker(model) + + val operationsWithConstrainedInputWithoutValidationException = model.serviceShapes + .filter { serviceShapeIdNameAllowList.contains(it.toShapeId().name) } + .flatMap { it.operations } + .map { model.expectShape(it, OperationShape::class.java) } + .filter { operationShape -> + // Walk the shapes reachable via this operation input. + walker.walkShapes(operationShape.inputShape(model)) + .any { it is SetShape || it is EnumShape || it.hasConstraintTrait() } + } + .filter { !it.errors.contains(ShapeId.from("smithy.framework#ValidationException")) } +// .map { it.errors } +// .forEach { +// it.errors.add(ShapeId.from("smithy.framework#ValidationException")) +// } +// val inputShapes = model.operationShapes.map { +// model.expectShape(it.inputShape, StructureShape::class.java) +// } +// val walker = Walker(model) +// val shapesReachableFromOperationInputs = inputShapes +// .flatMap { walker.walkShapes(it) } +// .toSet() + + return ModelTransformer.create().mapShapes(model) { shape -> + if (shape is OperationShape && operationsWithConstrainedInputWithoutValidationException.contains(shape)) { + shape.toBuilder().addError("smithy.framework#ValidationException").build() + } else { + shape + } + } +// for (errors in errorsInOperationsWithConstrainedInput) { +// errors.add(ShapeId.from("smithy.framework#ValidationException")) +// } +// return model + } +} diff --git a/codegen-core/common-test-models/smithy.framework.validation.smithy b/codegen-core/common-test-models/smithy.framework.validation.smithy new file mode 100644 index 0000000000..26532c655e --- /dev/null +++ b/codegen-core/common-test-models/smithy.framework.validation.smithy @@ -0,0 +1,36 @@ +// This file is a copy of +// https://github.com/awslabs/smithy/blob/7b2a980c73e87af3214044fe9ca7c391fb269633/smithy-validation-model/model/smithy.framework.validation.smithy + +$version: "2.0" + +namespace smithy.framework + +/// A standard error for input validation failures. +/// This should be thrown by services when a member of the input structure +/// falls outside of the modeled or documented constraints. +@error("client") +structure ValidationException { + + /// A summary of the validation failure. + @required + message: String, + + /// A list of specific failures encountered while validating the input. + /// A member can appear in this list more than once if it failed to satisfy multiple constraints. + fieldList: ValidationExceptionFieldList +} + +/// Describes one specific validation failure for an input member. +structure ValidationExceptionField { + /// A JSONPointer expression to the structure member whose value failed to satisfy the modeled constraints. + @required + path: String, + + /// A detailed description of the validation failure. + @required + message: String +} + +list ValidationExceptionFieldList { + member: ValidationExceptionField +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt index 85b4c77185..70aff870b8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt @@ -18,6 +18,10 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.traits.RangeTrait +import software.amazon.smithy.model.traits.RequiredTrait +import software.amazon.smithy.model.traits.UniqueItemsTrait import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.isOptional @@ -30,6 +34,18 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait * TODO Move this file to `core` or `server`. */ +/** + * Whether the shape has any trait that could cause a request to be rejected with a constraint violation, _whether + * we support it or not_. + */ +fun Shape.hasConstraintTrait() = + hasTrait() || + hasTrait() || + hasTrait() || + hasTrait() || + hasTrait() || + hasTrait() + /** * We say a shape is _directly_ constrained if: * diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index a36d42e8db..07b982f165 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -46,7 +46,7 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, ), CodegenTest("com.amazonaws.constraints#ConstraintsService", "constraints", imports = listOf("$commonModels/constraints.smithy")), - CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), + CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json", imports = listOf("$commonModels/smithy.framework.validation.smithy")), CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy")), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation", extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 103e974043..83e0e711bc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.client.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList import software.amazon.smithy.rust.codegen.client.smithy.transformers.AggregateShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -151,6 +152,8 @@ open class ServerCodegenVisitor( .let(RecursiveShapeBoxer::transform) // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) + // TODO Docs. + .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) // Tag aggregate shapes reachable from operation input. .let(AggregateShapesReachableFromOperationInputTagger::transform) // Normalize event stream operations diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 9c7ee9a943..6375cc3fdb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -240,7 +240,7 @@ class ServerRestJsonProtocol( } else { false to codegenContext.symbolProvider.toSymbol(shape) } - return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol, ::returnSymbolToParse) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = From cc3c71be9c610472a15a05e3d3bf34c862399b99 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 7 Oct 2022 13:34:45 +0200 Subject: [PATCH 221/255] Add ValidationException to the error set of the StoreServiceBlob operation --- ...xceptionToConstrainedOperationInputsInAllowList.kt | 9 +++++++-- codegen-core/common-test-models/simple.smithy | 11 +++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 6c3f10c621..c072125261 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -21,7 +21,12 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape */ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { // TODO Use fully qualified shapeIds - private val serviceShapeIdNameAllowList = setOf("RestJson") + private val sherviceShapeIdAllowList = + setOf( + ShapeId.from("aws.protocoltests.restjson#RestJson"), +// ShapeId.from("abacacacacacacac"), +// ShapeId.from("ababababababaaaaaaaaaaaaaa"), + ) fun transform(model: Model): Model { // model.serviceShapes.filter { it.toShapeId().name == "RestJsonValidation" }[0].operations.map { model.expectShape(it, OperationShape::class.java) }.map { it.errors.add( @@ -30,7 +35,7 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { val walker = Walker(model) val operationsWithConstrainedInputWithoutValidationException = model.serviceShapes - .filter { serviceShapeIdNameAllowList.contains(it.toShapeId().name) } + .filter { sherviceShapeIdAllowList.contains(it.toShapeId()) } .flatMap { it.operations } .map { model.expectShape(it, OperationShape::class.java) } .filter { operationShape -> diff --git a/codegen-core/common-test-models/simple.smithy b/codegen-core/common-test-models/simple.smithy index 2a0a7deb77..3d574206bf 100644 --- a/codegen-core/common-test-models/simple.smithy +++ b/codegen-core/common-test-models/simple.smithy @@ -5,6 +5,7 @@ namespace com.amazonaws.simple use aws.protocols#restJson1 use smithy.test#httpRequestTests use smithy.test#httpResponseTests +use smithy.framework#ValidationException @restJson1 @title("SimpleService") @@ -116,6 +117,7 @@ structure HealthcheckOutputResponse { operation StoreServiceBlob { input: StoreServiceBlobInput, output: StoreServiceBlobOutput + errors: [ValidationException] } @documentation("Store a blob for a service id input structure") @@ -126,16 +128,9 @@ structure StoreServiceBlobInput { @required @httpPayload content: Blob, - - @httpHeader("lengthString") - lengthString: LengthString, } @documentation("Store a blob for a service id output structure") structure StoreServiceBlobOutput { - @required - lengthString: LengthString -} -@length(min: 1, max: 68) -string LengthString +} From b013e624f2f4118442a4c8163834930e55d14953 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 7 Oct 2022 19:44:57 +0200 Subject: [PATCH 222/255] Add ValidationException to the error set of a bunch of services --- ...ToConstrainedOperationInputsInAllowList.kt | 6 +++-- .../RemoveEbsModelValidationException.kt | 24 +++++++++++++++++++ codegen-core/common-test-models/misc.smithy | 2 ++ .../naming-obstacle-course-ops.smithy | 6 ++++- .../common-test-models/pokemon-common.smithy | 4 +++- .../common-test-models/pokemon.smithy | 7 +++--- .../rest-json-extras.smithy | 7 ++++-- .../XmlBindingTraitSerializerGenerator.kt | 2 +- codegen-server-test/build.gradle.kts | 3 ++- .../server/smithy/ServerCodegenVisitor.kt | 3 +++ .../generators/ConstrainedStringGenerator.kt | 3 ++- .../MapConstraintViolationGenerator.kt | 1 + .../smithy/generators/ServerEnumGenerator.kt | 7 ++++-- 13 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index c072125261..78d9180d0b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -24,8 +24,10 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { private val sherviceShapeIdAllowList = setOf( ShapeId.from("aws.protocoltests.restjson#RestJson"), -// ShapeId.from("abacacacacacacac"), -// ShapeId.from("ababababababaaaaaaaaaaaaaa"), + ShapeId.from("aws.protocoltests.json10#JsonRpc10"), + ShapeId.from("aws.protocoltests.json#JsonProtocol"), + ShapeId.from("com.amazonaws.s3#AmazonS3"), + ShapeId.from("com.amazonaws.ebs#Ebs"), ) fun transform(model: Model): Model { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt new file mode 100644 index 0000000000..ba6591caba --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.core.util.orNull + +/** + * TODO Docs + * TODO Tag issue + * TODO Move to server + */ +object RemoveEbsModelValidationException { + fun transform(model: Model): Model { + val shapeToRemove = model.getShape(ShapeId.from("com.amazonaws.ebs#ValidationException")).orNull() + return ModelTransformer.create().removeShapes(model, listOfNotNull(shapeToRemove)) + } +} diff --git a/codegen-core/common-test-models/misc.smithy b/codegen-core/common-test-models/misc.smithy index c3449feb28..0ca37ad477 100644 --- a/codegen-core/common-test-models/misc.smithy +++ b/codegen-core/common-test-models/misc.smithy @@ -5,6 +5,7 @@ namespace aws.protocoltests.misc use aws.protocols#restJson1 use smithy.test#httpRequestTests use smithy.test#httpResponseTests +use smithy.framework#ValidationException /// A service to test miscellaneous aspects of code generation where protocol /// selection is not relevant. If you want to test something protocol-specific, @@ -57,6 +58,7 @@ map MapA { operation InnerRequiredShapeOperation { input: InnerRequiredShapeOperationInputOutput, output: InnerRequiredShapeOperationInputOutput, + errors: [ValidationException], } structure InnerRequiredShapeOperationInputOutput { diff --git a/codegen-core/common-test-models/naming-obstacle-course-ops.smithy b/codegen-core/common-test-models/naming-obstacle-course-ops.smithy index 087d99b750..f54b27e76f 100644 --- a/codegen-core/common-test-models/naming-obstacle-course-ops.smithy +++ b/codegen-core/common-test-models/naming-obstacle-course-ops.smithy @@ -5,6 +5,7 @@ use smithy.test#httpRequestTests use smithy.test#httpResponseTests use aws.protocols#awsJson1_1 use aws.api#service +use smithy.framework#ValidationException /// Confounds model generation machinery with lots of problematic names @awsJson1_1 @@ -41,17 +42,20 @@ service Config { } ]) operation ReservedWordsAsMembers { - input: ReservedWords + input: ReservedWords, + errors: [ValidationException], } // tests that module names are properly escaped operation Match { input: ReservedWords + errors: [ValidationException], } // Should generate a PascalCased `RpcEchoInput` struct. operation RPCEcho { input: ReservedWords + errors: [ValidationException], } structure ReservedWords { diff --git a/codegen-core/common-test-models/pokemon-common.smithy b/codegen-core/common-test-models/pokemon-common.smithy index 3198cb8c74..d213a16b15 100644 --- a/codegen-core/common-test-models/pokemon-common.smithy +++ b/codegen-core/common-test-models/pokemon-common.smithy @@ -2,6 +2,8 @@ $version: "1.0" namespace com.aws.example +use smithy.framework#ValidationException + /// A Pokémon species forms the basis for at least one Pokémon. @title("Pokémon Species") resource PokemonSpecies { @@ -17,7 +19,7 @@ resource PokemonSpecies { operation GetPokemonSpecies { input: GetPokemonSpeciesInput, output: GetPokemonSpeciesOutput, - errors: [ResourceNotFoundException], + errors: [ResourceNotFoundException, ValidationException], } @input diff --git a/codegen-core/common-test-models/pokemon.smithy b/codegen-core/common-test-models/pokemon.smithy index bd56468b91..d42185e31c 100644 --- a/codegen-core/common-test-models/pokemon.smithy +++ b/codegen-core/common-test-models/pokemon.smithy @@ -3,6 +3,7 @@ $version: "1.0" namespace com.aws.example.rust use aws.protocols#restJson1 +use smithy.framework#ValidationException use com.aws.example#PokemonSpecies use com.aws.example#GetServerStatistics use com.aws.example#DoNothing @@ -31,13 +32,13 @@ resource Storage { read: GetStorage, } -/// Retrieve information about your Pokedex. +/// Retrieve information about your Pokédex. @readonly @http(uri: "/pokedex/{user}", method: "GET") operation GetStorage { input: GetStorageInput, output: GetStorageOutput, - errors: [ResourceNotFoundException, NotAuthorized], + errors: [ResourceNotFoundException, NotAuthorized, ValidationException], } /// Not authorized to access Pokémon storage. @@ -74,7 +75,7 @@ structure GetStorageOutput { operation CapturePokemon { input: CapturePokemonEventsInput, output: CapturePokemonEventsOutput, - errors: [UnsupportedRegionError, ThrottlingError] + errors: [UnsupportedRegionError, ThrottlingError, ValidationException] } @input diff --git a/codegen-core/common-test-models/rest-json-extras.smithy b/codegen-core/common-test-models/rest-json-extras.smithy index 1d2fd701c2..b2c3b2153e 100644 --- a/codegen-core/common-test-models/rest-json-extras.smithy +++ b/codegen-core/common-test-models/rest-json-extras.smithy @@ -6,6 +6,7 @@ use aws.protocols#restJson1 use aws.api#service use smithy.test#httpRequestTests use smithy.test#httpResponseTests +use smithy.framework#ValidationException apply QueryPrecedence @httpRequestTests([ { @@ -153,7 +154,8 @@ structure PrimitiveIntDocument { ]) @http(uri: "/primitive", method: "POST") operation PrimitiveIntHeader { - output: PrimitiveIntHeaderInput + output: PrimitiveIntHeaderInput, + errors: [ValidationException], } integer PrimitiveInt @@ -175,7 +177,8 @@ structure PrimitiveIntHeaderInput { } ]) operation EnumQuery { - input: EnumQueryInput + input: EnumQueryInput, + errors: [ValidationException], } structure EnumQueryInput { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index 2a8ecc14d9..32c2fe21e6 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -441,7 +441,7 @@ class XmlBindingTraitSerializerGenerator( * ``` * * If [member] is not an optional shape, generate code like: - * `{ .. Block }` + * `{ .. BLOCK }` * * [inner] is passed a new `ctx` object to use for code generation which handles the * potentially new name of the input. diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 07b982f165..21f6ddb194 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -37,7 +37,8 @@ dependencies { val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> listOf( - CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), + // TODO Commented out because we should decide how to fix https://github.com/awslabs/smithy-rs/issues/1826 +// CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), CodegenTest("naming_obs_structs#NamingObstacleCourseStructs", "naming_test_structs", imports = listOf("$commonModels/naming-obstacle-course-structs.smithy")), CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), CodegenTest( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 83e0e711bc..cacb6cab8a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDe import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList import software.amazon.smithy.rust.codegen.client.smithy.transformers.AggregateShapesReachableFromOperationInputTagger +import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEbsModelValidationException import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget @@ -153,6 +154,8 @@ open class ServerCodegenVisitor( // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) // TODO Docs. + .let(RemoveEbsModelValidationException::transform) + // TODO Docs. .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) // Tag aggregate shapes reachable from operation input. .let(AggregateShapesReachableFromOperationInputTagger::transform) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 8fbd70cfcf..1a6f4a4941 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -161,8 +161,9 @@ class ConstrainedStringGenerator( """, ) + // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. rustBlock("impl ${constraintViolation.name}") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { rustBlock("match self") { rust( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index c92d18993a..ff38f5c650 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -79,6 +79,7 @@ class MapConstraintViolationGenerator( *constraintViolationCodegenScope, ) + // TODO Generate `as_validation_exception_field` only for maps in the operation input closure. rustBlock("impl $constraintViolationName") { rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { rustBlock("match self") { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index eea6793a70..77f27fe683 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.util.doubleQuote import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider @@ -57,12 +58,14 @@ open class ServerEnumGenerator( val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" + // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. + // TODO ValidationException should live under `crate::error::`. rustBlock("impl $constraintViolationName") { - rustBlockTemplate("pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { + rustBlockTemplate("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { rust( """ crate::model::ValidationExceptionField { - message: format!("$message", &self.0, &path), + message: format!(r##"$message"##, &self.0, &path), path, } """ From ccbbb35a18eabf4ac357e548ce100ff26c409ae2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 10 Oct 2022 11:19:13 +0200 Subject: [PATCH 223/255] Comment out s3 model; sa works --- codegen-server-test/build.gradle.kts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 21f6ddb194..7c9f8a6c65 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -59,7 +59,8 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> imports = listOf("$commonModels/ebs.json"), extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), - CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), + // TODO Commented out until https://github.com/awslabs/smithy-rs/issues/1831 is fixed. +// CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-server-sdk", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), ) } From 24315cdf9e9d465e57f182e8c0ca0bf132b2cf5a Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 10 Oct 2022 13:38:31 +0200 Subject: [PATCH 224/255] Calculate shapesReachableFromConstrainedOperationInputs in AggregateShapesReachableFromOperationInputTagger --- ...ShapesReachableFromOperationInputTagger.kt | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt index 63985ace8e..f08b76a222 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -7,11 +7,15 @@ package software.amazon.smithy.rust.codegen.client.smithy.transformers import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.SetShape +import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE @@ -36,25 +40,29 @@ import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE */ object AggregateShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { - val inputShapes = model.operationShapes.map { - model.expectShape(it.inputShape, StructureShape::class.java) - } + val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) } val walker = Walker(model) val shapesReachableFromOperationInputs = inputShapes .flatMap { walker.walkShapes(it) } .toSet() + val shapesReachableFromConstrainedOperationInputs = shapesReachableFromOperationInputs + .filter { it is SetShape || it is EnumShape || it.hasConstraintTrait() } return ModelTransformer.create().mapShapes(model) { shape -> when (shape) { - is StructureShape, is UnionShape, is ListShape, is MapShape -> { + is StructureShape, is UnionShape, is ListShape, is MapShape, is StringShape -> { + val builder = when (shape) { + is StructureShape -> shape.toBuilder() + is UnionShape -> shape.toBuilder() + is ListShape -> shape.toBuilder() + is MapShape -> shape.toBuilder() + is StringShape -> shape.toBuilder() + else -> UNREACHABLE("the `when` is exhaustive") + } + if (shapesReachableFromOperationInputs.contains(shape)) { - val builder = when (shape) { - is StructureShape -> shape.toBuilder() - is UnionShape -> shape.toBuilder() - is ListShape -> shape.toBuilder() - is MapShape -> shape.toBuilder() - else -> UNREACHABLE("the `when` is exhaustive") - } + builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() + } else if (shapesReachableFromConstrainedOperationInputs.contains(shape)) { builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() } else { shape From b45a6333d28b663482ade2cd6bd97d44af406acf Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 10 Oct 2022 13:38:33 +0200 Subject: [PATCH 225/255] Revert "Calculate shapesReachableFromConstrainedOperationInputs in AggregateShapesReachableFromOperationInputTagger" This reverts commit 24315cdf9e9d465e57f182e8c0ca0bf132b2cf5a. --- ...ShapesReachableFromOperationInputTagger.kt | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt index f08b76a222..63985ace8e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt @@ -7,15 +7,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.transformers import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker -import software.amazon.smithy.model.shapes.EnumShape import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.SetShape -import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE @@ -40,29 +36,25 @@ import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE */ object AggregateShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { - val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) } + val inputShapes = model.operationShapes.map { + model.expectShape(it.inputShape, StructureShape::class.java) + } val walker = Walker(model) val shapesReachableFromOperationInputs = inputShapes .flatMap { walker.walkShapes(it) } .toSet() - val shapesReachableFromConstrainedOperationInputs = shapesReachableFromOperationInputs - .filter { it is SetShape || it is EnumShape || it.hasConstraintTrait() } return ModelTransformer.create().mapShapes(model) { shape -> when (shape) { - is StructureShape, is UnionShape, is ListShape, is MapShape, is StringShape -> { - val builder = when (shape) { - is StructureShape -> shape.toBuilder() - is UnionShape -> shape.toBuilder() - is ListShape -> shape.toBuilder() - is MapShape -> shape.toBuilder() - is StringShape -> shape.toBuilder() - else -> UNREACHABLE("the `when` is exhaustive") - } - + is StructureShape, is UnionShape, is ListShape, is MapShape -> { if (shapesReachableFromOperationInputs.contains(shape)) { - builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() - } else if (shapesReachableFromConstrainedOperationInputs.contains(shape)) { + val builder = when (shape) { + is StructureShape -> shape.toBuilder() + is UnionShape -> shape.toBuilder() + is ListShape -> shape.toBuilder() + is MapShape -> shape.toBuilder() + else -> UNREACHABLE("the `when` is exhaustive") + } builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() } else { shape From 4ac0511d546726979c4c514df60b8b65bc3ab426 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 10 Oct 2022 20:02:29 +0200 Subject: [PATCH 226/255] Make `server-unit-tests` work --- .../ConstraintViolationSymbolProvider.kt | 1 + .../PubCrateConstrainedShapeSymbolProvider.kt | 2 + ...ToConstrainedOperationInputsInAllowList.kt | 3 +- ...hapesReachableFromOperationInputTagger.kt} | 23 ++- .../rust/codegen/core/smithy/Constraints.kt | 2 +- .../smithy/generators/StructureGenerator.kt | 2 +- .../generators/http/HttpBindingGenerator.kt | 4 +- .../protocols/parse/JsonParserGenerator.kt | 2 +- ...apeReachableFromOperationInputTagTrait.kt} | 4 +- .../smithy/rust/codegen/core/util/Smithy.kt | 4 - .../smithy/ConstrainedShapeSymbolProvider.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 8 +- .../UnconstrainedShapeSymbolProvider.kt | 4 +- .../ConstrainedMapGeneratorCommon.kt | 4 +- .../generators/ConstrainedStringGenerator.kt | 28 ++-- .../MapConstraintViolationGenerator.kt | 48 +++--- .../PubCrateConstrainedCollectionGenerator.kt | 8 +- .../PubCrateConstrainedMapGenerator.kt | 8 +- .../ServerBuilderConstraintViolations.kt | 10 +- .../generators/ServerBuilderGenerator.kt | 6 +- .../smithy/generators/ServerEnumGenerator.kt | 26 ++-- .../UnconstrainedCollectionGenerator.kt | 28 ++-- .../generators/UnconstrainedMapGenerator.kt | 4 +- .../generators/UnconstrainedUnionGenerator.kt | 19 ++- .../http/ServerResponseBindingGenerator.kt | 4 +- .../generators/protocol/ServerProtocol.kt | 2 +- .../ServerHttpBoundProtocolGenerator.kt | 2 +- .../server/smithy/protocols/ServerRestJson.kt | 2 +- .../smithy/testutil/ServerTestHelpers.kt | 14 +- .../ConstrainedShapeSymbolProviderTest.kt | 6 +- .../codegen/server/smithy/ConstraintsTest.kt | 6 +- ...CrateConstrainedShapeSymbolProviderTest.kt | 6 +- .../server/smithy/ServerInstantiatorTest.kt | 139 ++++++++++++------ .../UnconstrainedShapeSymbolProviderTest.kt | 9 +- ...ateUnsupportedConstraintsAreNotUsedTest.kt | 3 +- .../generators/ConstrainedMapGeneratorTest.kt | 57 +++---- .../ConstrainedStringGeneratorTest.kt | 34 +---- .../ServerCombinedErrorGeneratorTest.kt | 8 +- .../UnconstrainedCollectionGeneratorTest.kt | 31 ++-- .../UnconstrainedMapGeneratorTest.kt | 44 +++--- .../UnconstrainedUnionGeneratorTest.kt | 28 +--- .../EventStreamUnmarshallerGeneratorTest.kt | 12 +- 42 files changed, 341 insertions(+), 316 deletions(-) rename codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/{AggregateShapesReachableFromOperationInputTagger.kt => ShapesReachableFromOperationInputTagger.kt} (72%) rename codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/{AggregateShapeReachableFromOperationInputTagTrait.kt => ShapeReachableFromOperationInputTagTrait.kt} (90%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt index 1b29ca7d80..38b39a9a14 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Models import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.rustType diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt index dbc5ec2fac..e59432b536 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -23,6 +23,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing +import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.toPascalCase diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 78d9180d0b..53a14a6cee 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -12,7 +12,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.core.util.inputShape /** @@ -20,7 +20,6 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape * TODO Move to server */ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { - // TODO Use fully qualified shapeIds private val sherviceShapeIdAllowList = setOf( ShapeId.from("aws.protocoltests.restjson#RestJson"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt similarity index 72% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt index 63985ace8e..7f3188f122 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AggregateShapesReachableFromOperationInputTagger.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt @@ -9,32 +9,38 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE /** - * Tag all [aggregate shapes] reachable from operation input with the - * [AggregateShapeReachableFromOperationInputTagTrait] tag. + * Tag shapes reachable from operation input with the + * [ShapeReachableFromOperationInputTagTrait] tag. * * This is useful to determine whether we need to generate code to * enforce constraints upon request deserialization in the server. * * This needs to be a model transformer; it cannot be lazily calculated * when needed. This is because other model transformers may transform - * the model such that aggregate shapes that were reachable from operation + * the model such that shapes that were reachable from operation * input are no longer so. For example, [EventStreamNormalizer] pulls * event stream error variants out of the union shape where they are defined. - * As such, [AggregateShapesReachableFromOperationInputTagger] needs to run + * As such, [ShapesReachableFromOperationInputTagger] needs to run * before these model transformers. * + * WARNING: This transformer tags _all_ [aggregate shapes], and _some_ [simple shapes], + * but not all of them. Read the implementation to find out what shape types it + * currently tags. + * + * [simple shapes]: https://awslabs.github.io/smithy/2.0/spec/simple-types.html * [aggregate shapes]: https://awslabs.github.io/smithy/2.0/spec/aggregate-types.html#aggregate-types * * TODO Move this to `server`. */ -object AggregateShapesReachableFromOperationInputTagger { +object ShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { val inputShapes = model.operationShapes.map { model.expectShape(it.inputShape, StructureShape::class.java) @@ -46,16 +52,17 @@ object AggregateShapesReachableFromOperationInputTagger { return ModelTransformer.create().mapShapes(model) { shape -> when (shape) { - is StructureShape, is UnionShape, is ListShape, is MapShape -> { + is StructureShape, is UnionShape, is ListShape, is MapShape, is StringShape -> { if (shapesReachableFromOperationInputs.contains(shape)) { val builder = when (shape) { is StructureShape -> shape.toBuilder() is UnionShape -> shape.toBuilder() is ListShape -> shape.toBuilder() is MapShape -> shape.toBuilder() + is StringShape -> shape.toBuilder() else -> UNREACHABLE("the `when` is exhaustive") } - builder.addTrait(AggregateShapeReachableFromOperationInputTagTrait()).build() + builder.addTrait(ShapeReachableFromOperationInputTagTrait()).build() } else { shape } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt index 70aff870b8..0d5b17bc53 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy +package software.amazon.smithy.rust.codegen.core.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 945ad0b2e2..bfbe1a5dbd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -13,7 +13,7 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SensitiveTrait -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.asDeref diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 315eba5c98..c4e79566cd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -23,8 +23,8 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 9484fe452c..4ad2d55a47 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -22,7 +22,7 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt similarity index 90% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt rename to codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index d36a47d8b5..9b94bf151d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/AggregateShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -22,7 +22,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait * * See the [AggregateShapesReachableFromOperationInputTagger] model transform for how it's used. */ -class AggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { +class ShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { companion object { val ID = ShapeId.from("smithy.api.internal#syntheticStructureReachableFromOperationInputTag") } @@ -30,7 +30,7 @@ class AggregateShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { is StructureShape, is UnionShape, is ListShape, is MapShape -> { - shape.hasTrait() + shape.hasTrait() } else -> PANIC("this method does not support shape type ${shape.type}") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 40d07e7025..448107af0c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -8,9 +8,6 @@ package software.amazon.smithy.rust.codegen.core.util import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BooleanShape -import software.amazon.smithy.model.shapes.CollectionShape -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.NumberShape import software.amazon.smithy.model.shapes.OperationShape @@ -22,7 +19,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait -import software.amazon.smithy.rust.codegen.core.smithy.traits.AggregateShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 67bd3fb63f..779aa5b45d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -15,7 +15,7 @@ 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.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Models import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index cacb6cab8a..18d1d921bf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -23,11 +23,11 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList -import software.amazon.smithy.rust.codegen.client.smithy.transformers.AggregateShapesReachableFromOperationInputTagger +import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEbsModelValidationException import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -158,7 +158,7 @@ open class ServerCodegenVisitor( // TODO Docs. .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) // Tag aggregate shapes reachable from operation input. - .let(AggregateShapesReachableFromOperationInputTagger::transform) + .let(ShapesReachableFromOperationInputTagger::transform) // Normalize event stream operations .let(EventStreamNormalizer::transform) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index daec5b7317..6efc74de40 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -16,8 +16,8 @@ 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.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Default diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt index 2832290011..5a2b9d6c68 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt @@ -9,8 +9,8 @@ import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained /** * Common helper functions used in [UnconstrainedMapGenerator] and [MapConstraintViolationGenerator]. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 1a6f4a4941..a98108c199 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -19,7 +20,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage @@ -161,18 +164,19 @@ class ConstrainedStringGenerator( """, ) - // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. - rustBlock("impl ${constraintViolation.name}") { - rustBlock("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { - rustBlock("match self") { - rust( - """ - Self::Length(length) => crate::model::ValidationExceptionField { - message: format!("${lengthTrait.validationErrorMessage()}", length, &path), - path, - }, - """ - ) + if (shape.hasTrait()) { + rustBlock("impl ${constraintViolation.name}") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("match self") { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${lengthTrait.validationErrorMessage()}", length, &path), + path, + }, + """ + ) + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index ff38f5c650..15356f3abb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -8,12 +8,14 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -79,28 +81,30 @@ class MapConstraintViolationGenerator( *constraintViolationCodegenScope, ) - // TODO Generate `as_validation_exception_field` only for maps in the operation input closure. - rustBlock("impl $constraintViolationName") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { - rustBlock("match self") { - shape.getTrait()?.also { - rust( - """ - Self::Length(length) => crate::model::ValidationExceptionField { - message: format!("${it.validationErrorMessage()}", length, &path), - path, - }, - """) - } - if (isKeyConstrained(keyShape, symbolProvider)) { - // Note how we _do not_ append the key's member name to the path. This is intentional, as - // per the `RestJsonMalformedLengthMapKey` test. Note keys are always strings. - // https://github.com/awslabs/smithy/blob/ee0b4ff90daaaa5101f32da936c25af8c91cc6e9/smithy-aws-protocol-tests/model/restJson1/validation/malformed-length.smithy#L296-L295 - rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") - } - if (isValueConstrained(valueShape, model, symbolProvider)) { - // `as_str()` works with regular `String`s and constrained string shapes. - rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") + if (shape.hasTrait()) { + rustBlock("impl $constraintViolationName") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlock("match self") { + shape.getTrait()?.also { + rust( + """ + Self::Length(length) => crate::model::ValidationExceptionField { + message: format!("${it.validationErrorMessage()}", length, &path), + path, + }, + """ + ) + } + if (isKeyConstrained(keyShape, symbolProvider)) { + // Note how we _do not_ append the key's member name to the path. This is intentional, as + // per the `RestJsonMalformedLengthMapKey` test. Note keys are always strings. + // https://github.com/awslabs/smithy/blob/ee0b4ff90daaaa5101f32da936c25af8c91cc6e9/smithy-aws-protocol-tests/model/restJson1/validation/malformed-length.smithy#L296-L295 + rust("""Self::Key(key_constraint_violation) => key_constraint_violation.as_validation_exception_field(path),""") + } + if (isValueConstrained(valueShape, model, symbolProvider)) { + // `as_str()` works with regular `String`s and constrained string shapes. + rust("""Self::Value(key, value_constraint_violation) => value_constraint_violation.as_validation_exception_field(path + "/" + key.as_str()),""") + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 8d25e88a83..9618697536 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -13,10 +13,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType /** * A generator for a wrapper tuple newtype over a collection shape's symbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index a3d9738616..a4ee918f93 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -14,10 +14,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.isTransitivelyButNotDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType /** * A generator for a wrapper tuple newtype over a map shape's symbol type. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 845b701fd0..fcf2cb0815 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -4,7 +4,7 @@ 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.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait 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.toPascalCase @@ -62,8 +63,9 @@ class ServerBuilderConstraintViolations( renderImplDisplayConstraintViolation(writer) writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) - // TODO This is only needed if the structure shape is part of an operation input closure. - renderAsValidationExceptionFieldList(writer) + if (shape.hasTrait()) { + renderAsValidationExceptionFieldList(writer) + } } /** @@ -156,11 +158,9 @@ class ServerBuilderConstraintViolations( } } - // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. writer.rustTemplate( """ impl ConstraintViolation { - ##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { #{ValidationExceptionFieldWritable:W} } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 56ada0ef2d..098f4757fd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -10,9 +10,9 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled +import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 77f27fe683..31ef681402 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -13,9 +13,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator -import software.amazon.smithy.rust.codegen.core.util.doubleQuote +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -58,18 +59,19 @@ open class ServerEnumGenerator( val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" - // TODO Remove `dead_code` once we address this being generated only for shapes in operation input closure. // TODO ValidationException should live under `crate::error::`. - rustBlock("impl $constraintViolationName") { - rustBlockTemplate("##[allow(dead_code)] pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { - rust( - """ - crate::model::ValidationExceptionField { - message: format!(r##"$message"##, &self.0, &path), - path, - } - """ - ) + if (shape.hasTrait()) { + rustBlock("impl $constraintViolationName") { + rustBlockTemplate("pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { + rust( + """ + crate::model::ValidationExceptionField { + message: format!(r##"$message"##, &self.0, &path), + path, + } + """ + ) + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 2baafe7400..b2ebec6849 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -6,7 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -14,6 +14,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider /** @@ -105,7 +107,8 @@ class UnconstrainedCollectionGenerator( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), RustMetadata(visibility = constraintViolationVisibility), ) { - // TODO path + "/" + &self.0 instead of format! + // TODO We only need to generate `usize` when the collection shape is directly constrained: see + // UnconstrainedCollectionGeneratorTest for an example where it isn't. rustTemplate( """ ##[derive(Debug, PartialEq)] @@ -113,15 +116,22 @@ class UnconstrainedCollectionGenerator( pub(crate) usize, pub(crate) #{InnerConstraintViolationSymbol} ); - - impl $constraintViolationName { - pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { - self.1.as_validation_exception_field(format!("{}/{}", path, self.0)) - } - } """, - "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, + "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, ) + + if (shape.hasTrait()) { + rustTemplate( + """ + impl $constraintViolationName { + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { + self.1.as_validation_exception_field(path + "/" + &self.0) + } + } + """, + "String" to RuntimeType.String, + ) + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 9454b85617..ce21214f7b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -14,8 +14,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 9af0173f8c..b1333e134f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -21,12 +21,13 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait 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.toPascalCase @@ -139,11 +140,13 @@ class UnconstrainedUnionGenerator( constraintViolations().forEach { renderConstraintViolation(this, it) } } - rustBlock("impl $constraintViolationName") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { - withBlock("match self {", "}") { - for (constraintViolation in constraintViolations()) { - rust("""Self::${constraintViolation.name()}(inner) => inner.as_validation_exception_field(path + "/${constraintViolation.forMember.memberName}"),""") + if (shape.hasTrait()) { + rustBlock("impl $constraintViolationName") { + rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + withBlock("match self {", "}") { + for (constraintViolation in constraintViolations()) { + rust("""Self::${constraintViolation.name()}(inner) => inner.as_validation_exception_field(path + "/${constraintViolation.forMember.memberName}"),""") + } } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index b99e97a724..564825c97c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -9,11 +9,9 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization @@ -21,7 +19,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerResponseBindingGenerator( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 6375cc3fdb..2440d43af8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.rust diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 4a9e92fae5..2ca49362da 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -25,7 +25,7 @@ import software.amazon.smithy.model.traits.HttpErrorTrait import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.MediaTypeTrait -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index 213044de84..660dfbf174 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,7 +5,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols -import software.amazon.smithy.rust.codegen.client.smithy.workingWithPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 8e03c342f6..3f13478667 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig @@ -120,3 +120,15 @@ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: Ru modelBuilder.renderConvenienceMethod(this) } } + +// TODO Inline +fun serverTestModelBaselineTransform(model: Model) = + model +// .let(RecursiveShapeBoxer::transform) +// // Normalize operations by adding synthetic input and output shapes to every operation +// .let(OperationNormalizer::transform) +// // TODO Docs. +// .let(RemoveEbsModelValidationException::transform) +// // TODO Docs. +// .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) + .let(ShapesReachableFromOperationInputTagger::transform) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt index eec6073504..bcf7fe34ce 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProviderTest.kt @@ -10,9 +10,9 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.smithy.rustType -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index ac535855d4..c1643900b3 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -13,9 +13,9 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.client.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt index cb1f9be8c0..21baefe747 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProviderTest.kt @@ -14,9 +14,9 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.smithy.rustType -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProviders diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt index 85fdd06ac0..ffd06589ef 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt @@ -6,20 +6,25 @@ package software.amazon.smithy.rust.codegen.server.smithy import org.junit.jupiter.api.Test +import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.raw -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.rustlang.withBlock -import software.amazon.smithy.rust.codegen.client.smithy.generators.CodegenTarget -import software.amazon.smithy.rust.codegen.client.smithy.generators.Instantiator -import software.amazon.smithy.rust.codegen.client.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.client.smithy.transformers.RecursiveShapeBoxer -import software.amazon.smithy.rust.codegen.client.testutil.TestRuntimeConfig -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.raw +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator +import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer +import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider @@ -31,6 +36,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymb class ServerInstantiatorTest { private val model = """ namespace com.test + + use smithy.framework#ValidationException + @documentation("this documents the shape") structure MyStruct { foo: String, @@ -123,42 +131,81 @@ class ServerInstantiatorTest { val union = model.lookup("com.test#NestedUnion") val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.SERVER) val data = Node.parse("{}") - val writer = RustWriter.forModule("model") - structure.serverRenderWithModelBuilder(model, symbolProvider, writer) - inner.serverRenderWithModelBuilder(model, symbolProvider, writer) - nestedStruct.serverRenderWithModelBuilder(model, symbolProvider, writer) - UnionGenerator(model, symbolProvider, writer, union).render() - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) + + val project = TestWorkspace.testProject() + project.withModule(ModelsModule) { writer -> + structure.serverRenderWithModelBuilder(model, symbolProvider, writer) + inner.serverRenderWithModelBuilder(model, symbolProvider, writer) + nestedStruct.serverRenderWithModelBuilder(model, symbolProvider, writer) + UnionGenerator(model, symbolProvider, writer, union).render() + + writer.unitTest("server_instantiator_test") { + withBlock("let result = " , ";") { + sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) + } + + rust( + """ + use std::collections::HashMap; + use aws_smithy_types::{DateTime, Document}; + + let expected = MyStructRequired { + str: "".to_owned(), + primitive_int: 0, + int: 0, + ts: DateTime::from_secs(0), + byte: 0, + union: NestedUnion::Struct(NestedStruct { + str: "".into(), + num: 0, + }), + structure: NestedStruct { + str: "".into(), + num: 0, + }, + list: Vec::new(), + map: HashMap::new(), + doc: Document::Object(HashMap::new()), + }; + assert_eq!(result, expected); + """ + ) } - writer.write( - """ - use std::collections::HashMap; - use aws_smithy_types::{DateTime, Document}; - - let expected = MyStructRequired { - str: "".to_owned(), - primitive_int: 0, - int: 0, - ts: DateTime::from_secs(0), - byte: 0, - union: NestedUnion::Struct(NestedStruct { - str: "".into(), - num: 0, - }), - structure: NestedStruct { - str: "".into(), - num: 0, - }, - list: Vec::new(), - map: HashMap::new(), - doc: Document::Object(HashMap::new()), - }; - assert_eq!(result, expected); - """, - ) } - writer.compileAndTest() + project.compileAndTest() + + // TODO Remove +// writer.test { +// writer.withBlock("let result = ", ";") { +// sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) +// } +// writer.write( +// """ +// use std::collections::HashMap; +// use aws_smithy_types::{DateTime, Document}; +// +// let expected = MyStructRequired { +// str: "".to_owned(), +// primitive_int: 0, +// int: 0, +// ts: DateTime::from_secs(0), +// byte: 0, +// union: NestedUnion::Struct(NestedStruct { +// str: "".into(), +// num: 0, +// }), +// structure: NestedStruct { +// str: "".into(), +// num: 0, +// }, +// list: Vec::new(), +// map: HashMap::new(), +// doc: Document::Object(HashMap::new()), +// }; +// assert_eq!(result, expected); +// """, +// ) +// } +// writer.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index a87ae391e2..7c8efe9c17 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -9,11 +9,10 @@ import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustType -import software.amazon.smithy.rust.codegen.client.rustlang.render -import software.amazon.smithy.rust.codegen.client.smithy.UnconstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.rustType -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProviders diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 406a867e2c..8b57d5cb1d 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -10,8 +10,7 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenConfig -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import java.util.logging.Level diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index cffe575dac..3b493b0424 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -15,38 +15,20 @@ import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.client.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.client.smithy.ServerCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.Instantiator -import software.amazon.smithy.rust.codegen.client.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.client.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestModelBaselineTransform import java.util.stream.Stream -private const val baseModelString = """ - namespace test - - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - constrainedMap: ConstrainedMap - } - """ - class ConstrainedMapGeneratorTest { data class TestCase(val model: Model, val validMap: ObjectNode, val invalidMap: ObjectNode) @@ -65,19 +47,19 @@ class ConstrainedMapGeneratorTest { val validStringMap = List(it.second) { index -> index.toString() to "value" }.toMap() val inValidStringMap = List(it.third) { index -> index.toString() to "value" }.toMap() Triple(it.first, ObjectNode.fromStringMap(validStringMap), ObjectNode.fromStringMap(inValidStringMap)) - }.map { + }.map { (trait, validMap, invalidMap) -> TestCase( """ - $baseModelString + namespace test - ${it.first} + $trait map ConstrainedMap { key: String, value: String } - """.asSmithyModel(), - it.second, - it.third, + """.asSmithyModel().let { serverTestModelBaselineTransform(it) }, + validMap, + invalidMap, ) } @@ -88,10 +70,9 @@ class ConstrainedMapGeneratorTest { @ParameterizedTest @ArgumentsSource(ConstrainedMapGeneratorTestProvider::class) fun `it should generate constrained map types`(testCase: TestCase) { - val serviceShape = testCase.model.lookup("test#TestService") val constrainedMapShape = testCase.model.lookup("test#ConstrainedMap") - val codegenContext = serverTestCodegenContext(testCase.model, serviceShape) + val codegenContext = serverTestCodegenContext(testCase.model) val symbolProvider = codegenContext.symbolProvider val project = TestWorkspace.testProject(symbolProvider) @@ -163,14 +144,14 @@ class ConstrainedMapGeneratorTest { @Test fun `type should not be constructible without using a constructor`() { val model = """ - $baseModelString + namespace test @length(min: 1, max: 69) map ConstrainedMap { key: String, value: String } - """.asSmithyModel() + """.asSmithyModel().let { serverTestModelBaselineTransform(it) } val constrainedMapShape = model.lookup("test#ConstrainedMap") val writer = RustWriter.forModule(ModelsModule.name) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 7d6b9a055a..224b069e6f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -14,34 +14,16 @@ import org.junit.jupiter.params.provider.ArgumentsProvider import org.junit.jupiter.params.provider.ArgumentsSource import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.client.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.client.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.client.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import java.util.stream.Stream -private const val baseModelString = """ - namespace test - - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - constrainedString: ConstrainedString - } - """ - class ConstrainedStringGeneratorTest { data class TestCase(val model: Model, val validString: String, val invalidString: String) @@ -65,7 +47,7 @@ class ConstrainedStringGeneratorTest { ).map { TestCase( """ - $baseModelString + namespace test ${it.first} string ConstrainedString @@ -147,7 +129,7 @@ class ConstrainedStringGeneratorTest { @Test fun `type should not be constructible without using a constructor`() { val model = """ - $baseModelString + namespace test @length(min: 1, max: 69) string ConstrainedString diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt index 7007426a32..017ffb3561 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt @@ -59,8 +59,12 @@ class ServerCombinedErrorGeneratorTest { model.lookup("error#$it").serverRenderWithModelBuilder(model, symbolProvider, writer) } val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("error#$it") } - val generator = ServerCombinedErrorGenerator(model, symbolProvider, symbolProvider.toSymbol(model.lookup("error#Greeting")), errors) - generator.render(writer) + ServerCombinedErrorGenerator( + model, + symbolProvider, + symbolProvider.toSymbol(model.lookup("error#Greeting")), + errors, + ).render(writer) writer.unitTest( name = "generates_combined_error_enums", diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 98aed57b91..d813471542 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -8,12 +8,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustModule -import software.amazon.smithy.rust.codegen.client.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.client.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.client.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext @@ -25,20 +25,6 @@ class UnconstrainedCollectionGeneratorTest { """ namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - list: ListA - } - list ListA { member: ListB } @@ -78,6 +64,7 @@ class UnconstrainedCollectionGeneratorTest { UnconstrainedCollectionGenerator(codegenContext, unconstrainedModuleWriter, modelsModuleWriter, it).render() } + // TODO We should not have to pass in 0. See TODO in `UnconstrainedCollectionGenerator`. unconstrainedModuleWriter.unitTest( name = "list_a_unconstrained_fail_to_constrain_with_first_error", test = """ @@ -87,8 +74,8 @@ class UnconstrainedCollectionGeneratorTest { let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected_err = - crate::model::list_a::ConstraintViolation(crate::model::list_b::ConstraintViolation( - crate::model::structure_c::ConstraintViolation::MissingString, + crate::model::list_a::ConstraintViolation(0, crate::model::list_b::ConstraintViolation( + 0, crate::model::structure_c::ConstraintViolation::MissingString, )); assert_eq!( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 2d79b929f5..4e25835105 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -8,12 +8,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustModule -import software.amazon.smithy.rust.codegen.client.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.client.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.client.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext @@ -25,20 +25,6 @@ class UnconstrainedMapGeneratorTest { """ namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - map: MapA - } - map MapA { key: String, value: MapB @@ -100,14 +86,20 @@ class UnconstrainedMapGeneratorTest { ); // Any of these two errors could be returned; it depends on the order in which the maps are visited. - let missing_string_expected_err = - crate::model::map_a::ConstraintViolation::Value(crate::model::map_b::ConstraintViolation::Value( + let missing_string_expected_err = crate::model::map_a::ConstraintViolation::Value( + "KeyA".to_owned(), + crate::model::map_b::ConstraintViolation::Value( + "KeyB1".to_owned(), crate::model::structure_c::ConstraintViolation::MissingString, - )); - let missing_int_expected_err = - crate::model::map_a::ConstraintViolation::Value(crate::model::map_b::ConstraintViolation::Value( + ) + ); + let missing_int_expected_err = crate::model::map_a::ConstraintViolation::Value( + "KeyA".to_owned(), + crate::model::map_b::ConstraintViolation::Value( + "KeyB2".to_owned(), crate::model::structure_c::ConstraintViolation::MissingInt, - )); + ) + ); let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt index 0577b5babc..fedb327430 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGeneratorTest.kt @@ -8,13 +8,13 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.rustlang.RustModule -import software.amazon.smithy.rust.codegen.client.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.client.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.client.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.client.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.client.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.client.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule +import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext @@ -26,20 +26,6 @@ class UnconstrainedUnionGeneratorTest { """ namespace test - service TestService { - version: "123", - operations: [TestOperation] - } - - operation TestOperation { - input: TestInputOutput, - output: TestInputOutput, - } - - structure TestInputOutput { - union: Union - } - union Union { structure: Structure } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt index c69c17cf63..c4d198f464 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt @@ -7,10 +7,13 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols.parse import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest @@ -34,7 +37,14 @@ class EventStreamUnmarshallerGeneratorTest { target = testCase.target, ) val protocol = testCase.protocolBuilder(codegenContext) - val generator = EventStreamUnmarshallerGenerator(protocol, codegenContext, test.operationShape, test.streamShape) + fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) + val generator = EventStreamUnmarshallerGenerator( + protocol, + codegenContext, + test.operationShape, + test.streamShape, + ::builderSymbol, + ) test.project.lib { writer -> writer.rust( From 4bd38a1a5ca7db846c2b277651c3a6323aa45600 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 13:25:50 +0200 Subject: [PATCH 227/255] Fix spst and python-tests --- .../python/smithy/PythonCodegenServerPlugin.kt | 2 -- .../python/smithy/PythonServerCodegenVisitor.kt | 4 +--- .../smithy/generators/PythonServerEnumGenerator.kt | 4 +--- .../generators/ServerBuilderConstraintViolations.kt | 12 ++++++++---- .../smithy/generators/ServerBuilderGenerator.kt | 13 ++++++++++--- ...BuilderGeneratorWithoutPublicConstrainedTypes.kt | 7 ++++++- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt index 9fca95ef42..218eb8af1b 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonCodegenServerPlugin.kt @@ -18,8 +18,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS -import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerSymbolVisitor -import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonStreamingShapeMetadataProvider import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index adc8d47c48..75e67dd1d1 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -16,13 +16,11 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.DefaultPublicModules import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerServiceGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt index ed2855e39d..cad7bad677 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEnumGenerator.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -15,10 +14,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index fcf2cb0815..35a8b38fff 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait 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.toPascalCase @@ -52,7 +51,12 @@ class ServerBuilderConstraintViolations( ) } - fun render(writer: RustWriter, visibility: Visibility, nonExhaustive: Boolean) { + fun render( + writer: RustWriter, + visibility: Visibility, + nonExhaustive: Boolean, + shouldRenderAsValidationExceptionFieldList: Boolean, + ) { Attribute.Derives(setOf(RuntimeType.Debug, RuntimeType.PartialEq)).render(writer) writer.docs("Holds one variant for each of the ways the builder can fail.") if (nonExhaustive) Attribute.NonExhaustive.render(writer) @@ -63,7 +67,7 @@ class ServerBuilderConstraintViolations( renderImplDisplayConstraintViolation(writer) writer.rust("impl #T for ConstraintViolation { }", RuntimeType.StdError) - if (shape.hasTrait()) { + if (shouldRenderAsValidationExceptionFieldList) { renderAsValidationExceptionFieldList(writer) } } @@ -148,8 +152,8 @@ class ServerBuilderConstraintViolations( rust( """ ConstraintViolation::${it.name()} => crate::model::ValidationExceptionField { - path: format!("{}/${it.forMember.memberName}", path), message: format!("Value null at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), + path: path + "/${it.forMember.memberName}", }, """ ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 098f4757fd..3ff28074cb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -120,11 +120,18 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - serverBuilderConstraintViolations.render(writer, visibility, nonExhaustive = true) - // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is // an operation input shape. - if (shape.hasTrait()) { + val shouldConvertConstraintViolationToRequestRejection = shape.hasTrait() + + serverBuilderConstraintViolations.render( + writer, + visibility, + nonExhaustive = true, + shouldRenderAsValidationExceptionFieldList = shouldConvertConstraintViolationToRequestRejection, + ) + + if (shouldConvertConstraintViolationToRequestRejection) { renderImplFromConstraintViolationForRequestRejection(writer) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 0bd2f8cd03..1ef269a63a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -74,7 +74,12 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - serverBuilderConstraintViolations.render(writer, Visibility.PUBLIC, nonExhaustive = false) + serverBuilderConstraintViolations.render( + writer, + Visibility.PUBLIC, + nonExhaustive = false, + shouldRenderAsValidationExceptionFieldList = false, + ) renderTryFromBuilderImpl(writer) } else { From 7b177bfad3dbcb2f2471575f07ae026e94f37ef2 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 13:36:57 +0200 Subject: [PATCH 228/255] Fix server-e2e-tests --- .../aws-smithy-http-server/examples/pokemon-service/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs index 6b576c6ac2..e0d0788e35 100644 --- a/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/lib.rs @@ -213,7 +213,7 @@ pub async fn capture_pokemon( mut input: input::CapturePokemonInput, ) -> Result { if input.region != "Kanto" { - return Err(error::CapturePokemonOperationError::UnsupportedRegionError( + return Err(error::CapturePokemonError::UnsupportedRegionError( error::UnsupportedRegionError { region: input.region }, )); } From 09b22dac2cf2aa8116ba2d92ff5b308e6f3a9ff5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 13:37:57 +0200 Subject: [PATCH 229/255] ./gradlew ktlintFormat --- .../RemoveEbsModelValidationException.kt | 1 - .../smithy/rust/codegen/core/smithy/Constraints.kt | 13 +++++-------- ...ServerBuilderSymbolButThisShouldNotStayInCore.kt | 1 - .../core/smithy/generators/StructureGenerator.kt | 2 +- .../smithy/generators/http/HttpBindingGenerator.kt | 4 ++-- .../smithy/protocols/parse/JsonParserGenerator.kt | 2 +- .../protocols/serialize/JsonSerializerGenerator.kt | 1 + .../ShapeReachableFromOperationInputTagTrait.kt | 1 - .../server/smithy/ConstrainedShapeSymbolProvider.kt | 2 +- .../PubCrateConstraintViolationSymbolProvider.kt | 2 +- .../codegen/server/smithy/ServerCodegenVisitor.kt | 6 +++--- .../smithy/UnconstrainedShapeSymbolProvider.kt | 4 ++-- .../smithy/generators/ConstrainedStringGenerator.kt | 3 +-- .../generators/MapConstraintViolationGenerator.kt | 5 ++--- .../PubCrateConstrainedCollectionGenerator.kt | 2 +- .../generators/PubCrateConstrainedMapGenerator.kt | 2 +- .../generators/ServerBuilderConstraintViolations.kt | 6 +++--- .../smithy/generators/ServerBuilderGenerator.kt | 6 +++--- ...BuilderGeneratorWithoutPublicConstrainedTypes.kt | 2 +- .../server/smithy/generators/ServerEnumGenerator.kt | 2 +- .../generators/UnconstrainedCollectionGenerator.kt | 6 +++--- .../smithy/generators/UnconstrainedMapGenerator.kt | 2 +- .../generators/UnconstrainedUnionGenerator.kt | 4 ++-- .../http/ServerRequestBindingGenerator.kt | 2 -- .../http/ServerResponseBindingGenerator.kt | 2 +- .../smithy/generators/protocol/ServerProtocol.kt | 2 +- .../server/smithy/protocols/ServerAwsJson.kt | 1 - .../protocols/ServerHttpBoundProtocolGenerator.kt | 2 +- .../server/smithy/protocols/ServerRestJson.kt | 2 +- .../codegen/server/smithy/ServerInstantiatorTest.kt | 5 ++--- .../generators/ConstrainedMapGeneratorTest.kt | 2 +- .../generators/ServerCombinedErrorGeneratorTest.kt | 2 -- .../smithy/generators/ServerEnumGeneratorTest.kt | 2 -- .../parse/EventStreamUnmarshallerGeneratorTest.kt | 1 - 34 files changed, 43 insertions(+), 59 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt index ba6591caba..58d17645d0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.transformers import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.util.orNull diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt index 0d5b17bc53..f4bc332e92 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt @@ -22,9 +22,6 @@ import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.UniqueItemsTrait -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -40,11 +37,11 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait */ fun Shape.hasConstraintTrait() = hasTrait() || - hasTrait() || - hasTrait() || - hasTrait() || - hasTrait() || - hasTrait() + hasTrait() || + hasTrait() || + hasTrait() || + hasTrait() || + hasTrait() /** * We say a shape is _directly_ constrained if: diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt index b815296444..522ecba1d7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt @@ -24,4 +24,3 @@ fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: .definitionFile(structureSymbol.definitionFile) .build() } - diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index bfbe1a5dbd..f38edae609 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SensitiveTrait -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.asDeref @@ -29,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index c4e79566cd..216f99d1da 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -23,8 +23,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.MediaTypeTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -42,6 +40,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError @@ -52,6 +51,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 4ad2d55a47..b2131bdadf 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SparseTrait import software.amazon.smithy.model.traits.TimestampFormatTrait -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -48,6 +47,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.deserializeFunctionName +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index 91d83fcd94..ae0a4b2ed1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -56,6 +56,7 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape sealed class JsonSection(name: String) : Section(name) { /** Mutate the server error object prior to finalization. Eg: this can be used to inject `__type` to record the error type. */ data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSection("ServerError") + // TODO This could take in directly the `Context` data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSection("BeforeIteratingOverMap") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index 9b94bf151d..c650771ab7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -38,4 +38,3 @@ fun StructureShape.isReachableFromOperationInput() = isShapeReachableFromOperati fun CollectionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) fun UnionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) fun MapShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) - diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 779aa5b45d..7e4f9a3960 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -15,7 +15,6 @@ 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.traits.LengthTrait -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Models import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider @@ -23,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt index f6526973cb..bdc2535225 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -7,8 +7,8 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.rustType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 18d1d921bf..35ec69fe87 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -23,12 +23,10 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.client.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList -import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEbsModelValidationException +import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget @@ -39,9 +37,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index 6efc74de40..170d2344ec 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -16,20 +16,20 @@ 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.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.setDefault import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index a98108c199..528c540c4e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -174,7 +173,7 @@ class ConstrainedStringGenerator( message: format!("${lengthTrait.validationErrorMessage()}", length, &path), path, }, - """ + """, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 15356f3abb..4ca0d5e6d4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -16,10 +15,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage class MapConstraintViolationGenerator( @@ -92,7 +91,7 @@ class MapConstraintViolationGenerator( message: format!("${it.validationErrorMessage()}", length, &path), path, }, - """ + """, ) } if (isKeyConstrained(keyShape, symbolProvider)) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 9618697536..cc8e7a7064 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -12,11 +12,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * A generator for a wrapper tuple newtype over a collection shape's symbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index a4ee918f93..ad59ce368f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -13,11 +13,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * A generator for a wrapper tuple newtype over a map shape's symbol type. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 35a8b38fff..c14f41e924 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -4,7 +4,6 @@ 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.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -14,14 +13,15 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait 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.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * Renders constraint violation types that arise when building a structure shape builder. @@ -155,7 +155,7 @@ class ServerBuilderConstraintViolations( message: format!("Value null at '{}/${it.forMember.memberName}' failed to satisfy constraint: Member must not be null", path), path: path + "/${it.forMember.memberName}", }, - """ + """, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 3ff28074cb..f0a09ec5bc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -10,9 +10,6 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -34,6 +31,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -41,8 +39,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput +import software.amazon.smithy.rust.codegen.core.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled 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 diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 1ef269a63a..3ecca95a14 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -20,12 +20,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 31ef681402..fa6ac30f3b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -69,7 +69,7 @@ open class ServerEnumGenerator( message: format!(r##"$message"##, &self.0, &path), path, } - """ + """, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index b2ebec6849..4b7b1327dd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -6,17 +6,17 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.CollectionShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding @@ -117,7 +117,7 @@ class UnconstrainedCollectionGenerator( pub(crate) #{InnerConstraintViolationSymbol} ); """, - "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, + "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, ) if (shape.hasTrait()) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index ce21214f7b..c303ac647e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -13,11 +13,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index b1333e134f..85cbde1228 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -20,18 +20,18 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed +import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait 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.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 6d4a5b28b0..35b22fa888 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -9,9 +9,7 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -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.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index 564825c97c..2e4056fa68 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -48,7 +48,7 @@ class ServerResponseBindingGenerator( } // TODO Docs -class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext): HttpBindingCustomization() { +class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext) : HttpBindingCustomization() { override fun section(section: HttpBindingSection): Writable = when (section) { is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders -> writable { if (workingWithPublicConstrainedWrapperTupleType(section.shape, codegenContext.model, codegenContext.settings.codegenConfig.publicConstrainedTypes)) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 2440d43af8..fbbb64f135 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -19,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index f0abb2d076..aecaf3ed51 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 2ca49362da..ee63b920f3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -25,7 +25,6 @@ import software.amazon.smithy.model.traits.HttpErrorTrait import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.MediaTypeTrait -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -44,6 +43,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index 660dfbf174..ee4c43d69e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols -import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -18,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonC import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator +import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt index ffd06589ef..550231b94c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import org.junit.jupiter.api.Test -import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape @@ -140,7 +139,7 @@ class ServerInstantiatorTest { UnionGenerator(model, symbolProvider, writer, union).render() writer.unitTest("server_instantiator_test") { - withBlock("let result = " , ";") { + withBlock("let result = ", ";") { sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) } @@ -168,7 +167,7 @@ class ServerInstantiatorTest { doc: Document::Object(HashMap::new()), }; assert_eq!(result, expected); - """ + """, ) } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 3b493b0424..20946494f4 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -18,13 +18,13 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestModelBaselineTransform import java.util.stream.Stream diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt index 017ffb3561..5033e687d9 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt @@ -8,13 +8,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ServerCombinedErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt index face5091c5..0e813cebbd 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGeneratorTest.kt @@ -9,10 +9,8 @@ import io.kotest.matchers.string.shouldNotContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt index c4d198f464..4ed29af80b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/parse/EventStreamUnmarshallerGeneratorTest.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator -import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.testRustSettings import software.amazon.smithy.rust.codegen.core.testutil.unitTest From 8e8c59d2c2395a842aeefa5e20456d434d74b5ca Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 15:01:53 +0200 Subject: [PATCH 230/255] Tackle a few TODOs Mainly add documentation and shuffle things among modules. --- .../RemoveEbsModelValidationException.kt | 23 -------- .../ConstraintsButThisShouldNotStayInCore.kt | 56 +++++++++++++++++++ ...BuilderSymbolButThisShouldNotStayInCore.kt | 26 --------- .../smithy/generators/BuilderGenerator.kt | 3 +- .../smithy/generators/StructureGenerator.kt | 20 +------ .../ConstraintViolationSymbolProvider.kt | 10 ++-- .../codegen/server}/smithy/Constraints.kt | 49 +--------------- .../PubCrateConstrainedShapeSymbolProvider.kt | 6 +- ...bCrateConstraintViolationSymbolProvider.kt | 1 - .../server/smithy/ServerCodegenContext.kt | 2 - .../server/smithy/ServerCodegenVisitor.kt | 13 +++-- .../server/smithy/ServerSymbolProviders.kt | 9 ++- .../UnconstrainedShapeSymbolProvider.kt | 9 +-- ...BeforeIteratingOverMapJsonCustomization.kt | 33 +++++++++++ .../PubCrateConstrainedCollectionGenerator.kt | 4 +- .../PubCrateConstrainedMapGenerator.kt | 4 +- .../generators/ServerBuilderGenerator.kt | 5 +- ...rGeneratorWithoutPublicConstrainedTypes.kt | 1 - .../smithy/generators/ServerBuilderSymbol.kt | 23 +++++++- .../ServerStructureConstrainedTraitImpl.kt | 1 - .../UnconstrainedCollectionGenerator.kt | 2 +- .../http/ServerRequestBindingGenerator.kt | 2 +- .../http/ServerResponseBindingGenerator.kt | 11 +++- .../server/smithy/protocols/ServerAwsJson.kt | 3 +- .../server/smithy/protocols/ServerRestJson.kt | 27 +-------- .../smithy/testutil/ServerTestHelpers.kt | 14 +---- ...ToConstrainedOperationInputsInAllowList.kt | 37 +++++------- .../RemoveEbsModelValidationException.kt | 38 +++++++++++++ ...ShapesReachableFromOperationInputTagger.kt | 4 +- .../codegen/server/smithy/ConstraintsTest.kt | 2 - .../generators/ConstrainedMapGeneratorTest.kt | 6 +- 31 files changed, 215 insertions(+), 229 deletions(-) delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt create mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt delete mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt rename {codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/ConstraintViolationSymbolProvider.kt (94%) rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/Constraints.kt (63%) rename {codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/PubCrateConstrainedShapeSymbolProvider.kt (95%) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt rename {codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt (64%) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RemoveEbsModelValidationException.kt rename {codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/transformers/ShapesReachableFromOperationInputTagger.kt (97%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt deleted file mode 100644 index 58d17645d0..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/RemoveEbsModelValidationException.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.smithy.transformers - -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.util.orNull - -/** - * TODO Docs - * TODO Tag issue - * TODO Move to server - */ -object RemoveEbsModelValidationException { - fun transform(model: Model): Model { - val shapeToRemove = model.getShape(ShapeId.from("com.amazonaws.ebs#ValidationException")).orNull() - return ModelTransformer.create().removeShapes(model, listOfNotNull(shapeToRemove)) - } -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt new file mode 100644 index 0000000000..f064d424de --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt @@ -0,0 +1,56 @@ +package software.amazon.smithy.rust.codegen.core.smithy + +import software.amazon.smithy.codegen.core.SymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape +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.model.traits.EnumTrait +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait + +// TODO Move to `Constraints.kt` in `codegen-server`. I can't yet because we need to split `Instantiator.kt`. + +fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + if (this is MemberShape) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented + // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, + // so we can't simply delegate to the `else` branch when we implement them. + this.targetCanReachConstrainedShape(model, symbolProvider) + } else { + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } + } + +fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) + +/** + * We say a shape is _directly_ constrained if: + * + * - it has a constraint trait, or; + * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. + * + * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is + * a constrained shape is _not_ directly constrained. + * + * At the moment only a subset of constraint traits are implemented on a subset of shapes; that's why we match against + * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's + * (with these subsets being smaller than what [the spec] accounts for). + * + * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html + */ +fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when (this) { + is StructureShape -> { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): + // The only reason why the functions in this file have + // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. + this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } + } + is MapShape -> this.hasTrait() + is StringShape -> this.hasTrait() || this.hasTrait() + else -> false +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt deleted file mode 100644 index 522ecba1d7..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ServerBuilderSymbolButThisShouldNotStayInCore.kt +++ /dev/null @@ -1,26 +0,0 @@ -package software.amazon.smithy.rust.codegen.core.smithy - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.codegen.core.SymbolProvider -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords -import software.amazon.smithy.rust.codegen.core.rustlang.RustType -import software.amazon.smithy.rust.codegen.core.util.toSnakeCase - -// TODO Move this to `ServerBuilderSymbol` in codegen-server -fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: Boolean): Symbol { - val structureSymbol = symbolProvider.toSymbol(this) - val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + - if (pubCrate) { - "_internal" - } else { - "" - } - val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") - return Symbol.builder() - .rustType(rustType) - .name(rustType.name) - .namespace(rustType.namespace, "::") - .definitionFile(structureSymbol.definitionFile) - .build() -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index cd2369c5af..17c3cfc3a3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -35,7 +35,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO Move this to `client`. +// TODO Move this entire file to `codegen-client`. + fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index f38edae609..b8e6dd362d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -24,7 +24,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.isDeref import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider @@ -35,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGen import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -57,22 +55,6 @@ fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): S } } -// TODO Delete this -fun StructureShape.builderSymbol( - codegenContext: CodegenContext, - symbolProvider: RustSymbolProvider, -) = - // TODO We assume we're in the server and `publicConstrainedTypes` is `true` just so that it compiles and we can test, - // the correct implementation is commented. - this.serverBuilderSymbol(codegenContext.symbolProvider, false) -// when (codegenContext.target) { -// CodegenTarget.CLIENT -> this.builderSymbol(symbolProvider) -// CodegenTarget.SERVER -> { -// val publicConstrainedTypes = (codegenContext as ServerCodegenContext).settings.codegenConfig.publicConstrainedTypes -// this.serverBuilderSymbol(symbolProvider, !publicConstrainedTypes) -// } -// } - open class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, @@ -114,6 +96,8 @@ open class StructureGenerator( /** * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a * fallible builder to be constructed. + * + * TODO Move this to `codegen-server`. I can't yet because we need to split `Instantiator.kt`. */ fun serverHasFallibleBuilder( structureShape: StructureShape, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt similarity index 94% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 38b39a9a14..4af4d55514 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy +package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model @@ -21,11 +21,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.contextName -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.toSnakeCase - -// TODO Move this file to `core` or `server`. +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol /** * The [ConstraintViolationSymbolProvider] returns, for a given constrained @@ -64,6 +62,7 @@ class ConstraintViolationSymbolProvider( private val base: RustSymbolProvider, private val model: Model, private val serviceShape: ServiceShape, + private val publicConstrainedTypes: Boolean, ) : WrappingSymbolProvider(base) { private val constraintViolationName = "ConstraintViolation" @@ -94,8 +93,7 @@ class ConstraintViolationSymbolProvider( constraintViolationSymbolForCollectionOrMapOrUnionShape(shape) } is StructureShape -> { - // TODO This should work with serverBuilderSymbol - val builderSymbol = shape.builderSymbol(base) + val builderSymbol = shape.serverBuilderSymbol(base, pubCrate = !publicConstrainedTypes) val namespace = builderSymbol.namespace val rustType = RustType.Opaque(constraintViolationName, namespace) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt similarity index 63% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt index f4bc332e92..2fb5671879 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/Constraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt @@ -3,11 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy +package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model -import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape @@ -22,13 +21,13 @@ import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.UniqueItemsTrait +import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * This file contains utilities to work with constrained shapes. - * - * TODO Move this file to `core` or `server`. */ /** @@ -43,34 +42,6 @@ fun Shape.hasConstraintTrait() = hasTrait() || hasTrait() -/** - * We say a shape is _directly_ constrained if: - * - * - it has a constraint trait, or; - * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. - * - * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is - * a constrained shape is _not_ directly constrained. - * - * At the moment only a subset of constraint traits are implemented on a subset of shapes; that's why we match against - * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's - * (with these subsets being smaller than what [the spec] accounts for). - * - * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html - */ -fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when (this) { - is StructureShape -> { - // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): - // The only reason why the functions in this file have - // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as - // `required`, so we can't use `member.isOptional` here. - this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } - } - is MapShape -> this.hasTrait() - is StringShape -> this.hasTrait() || this.hasTrait() - else -> false -} - fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTypes: Boolean): Boolean = when (this) { is MapShape -> publicConstrainedTypes && this.hasTrait() is StringShape -> !this.hasTrait() && (publicConstrainedTypes && this.hasTrait()) @@ -87,7 +58,6 @@ fun Shape.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled( * This function is used in core code generators, so it takes in a [CodegenContext] that is downcast * to [ServerCodegenContext] when generating servers. */ -// TODO Move this to the `codegen-server`. In fact, try to move everything to `codegen-server`. fun workingWithPublicConstrainedWrapperTupleType(shape: Shape, model: Model, publicConstrainedTypes: Boolean): Boolean = shape.hasPublicConstrainedWrapperTupleType(model, publicConstrainedTypes) @@ -115,21 +85,8 @@ fun Shape.typeNameContainsNonPublicType( else -> UNREACHABLE("the above arms should be exhaustive, but we received shape: $this") } -fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) - fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider): Boolean = this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider): Boolean = !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) - -fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - if (this is MemberShape) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented - // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, - // so we can't simply delegate to the `else` branch when we implement them. - this.targetCanReachConstrainedShape(model, symbolProvider) - } else { - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt similarity index 95% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt index e59432b536..e63e18c7ac 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/PubCrateConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstrainedShapeSymbolProvider.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy +package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model @@ -23,15 +23,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing -import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait -import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO Move this to `core` or `server`. - /** * The [PubCrateConstrainedShapeSymbolProvider] returns, for a given * _transitively but not directly_ constrained shape, a symbol whose Rust type diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt index bdc2535225..afc5030242 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PubCrateConstraintViolationSymbolProvider.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.rustType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt index 48a83a7af5..a0ad38f04f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenContext.kt @@ -8,8 +8,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 35ec69fe87..d164f28074 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -24,9 +24,9 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList -import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEbsModelValidationException -import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger +import software.amazon.smithy.rust.codegen.server.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList +import software.amazon.smithy.rust.codegen.server.smithy.transformers.RemoveEbsModelValidationException +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget @@ -153,11 +153,12 @@ open class ServerCodegenVisitor( .let(RecursiveShapeBoxer::transform) // Normalize operations by adding synthetic input and output shapes to every operation .let(OperationNormalizer::transform) - // TODO Docs. + // Remove the EBS model's own `ValidationException`, which collides with `smithy.framework#ValidationException` .let(RemoveEbsModelValidationException::transform) - // TODO Docs. + // Attach the `smithy.framework#ValidationException` error to operations whose inputs are constrained, + // if they belong to a service in an allowlist .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) - // Tag aggregate shapes reachable from operation input. + // Tag aggregate shapes reachable from operation input .let(ShapesReachableFromOperationInputTagger::transform) // Normalize event stream operations .let(EventStreamNormalizer::transform) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt index cb9908a6f8..e2b77c90fd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerSymbolProviders.kt @@ -7,8 +7,6 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.rust.codegen.client.smithy.ConstraintViolationSymbolProvider -import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -55,7 +53,12 @@ class ServerSymbolProviders private constructor( model, service, ), - constraintViolationSymbolProvider = ConstraintViolationSymbolProvider(baseSymbolProvider, model, service), + constraintViolationSymbolProvider = ConstraintViolationSymbolProvider( + baseSymbolProvider, + model, + service, + publicConstrainedTypes, + ), ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index 170d2344ec..4b1673ac0b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -26,12 +26,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.core.smithy.setDefault import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol /** * The [UnconstrainedShapeSymbolProvider] returns, _for a given constrained @@ -73,13 +73,6 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * symbol provider _not_ wrap [PublicConstrainedShapeSymbolProvider] (from the * `codegen-server` subproject), because that symbol provider will return a * constrained type for shapes that have constraint traits attached. - * - * TODO Move this to `server`; remove below sentence. - * - * TODO This sentence below is not true now - * While this symbol provider is only used by the server, it needs to be in the - * `codegen` subproject because the (common to client and server) parsers use - * it. */ class UnconstrainedShapeSymbolProvider( private val base: RustSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt new file mode 100644 index 0000000000..62fca7d9e7 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt @@ -0,0 +1,33 @@ +package software.amazon.smithy.rust.codegen.server.smithy.customizations + +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection +import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext + +/** + * A customization to, just before we iterate over a _constrained_ map shape in a JSON serializer, unwrap the wrapper + * newtype and take a shared reference to the actual `std::collections::HashMap` within it. + */ +class BeforeIteratingOverMapJsonCustomization(private val codegenContext: ServerCodegenContext) : JsonCustomization() { + override fun section(section: JsonSection): Writable = when (section) { + is JsonSection.ServerError -> emptySection + is JsonSection.BeforeIteratingOverMap -> writable { + if (workingWithPublicConstrainedWrapperTupleType( + section.shape, + codegenContext.model, + codegenContext.settings.codegenConfig.publicConstrainedTypes, + ) + ) { + // Note that this particular implementation just so happens to work because when the customization + // is invoked in the JSON serializer, the value expression is guaranteed to be a variable binding name. + // If the expression in the future were to be more complex, we wouldn't be able to write the left-hand + // side of this assignment. + rust("""let ${section.valueExpression.name} = &${section.valueExpression.name}.0;""") + } + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index cc8e7a7064..9373cbecb2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -14,8 +14,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained -import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index ad59ce368f..262e8790f8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -15,8 +15,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained -import software.amazon.smithy.rust.codegen.core.smithy.isTransitivelyButNotDirectlyConstrained -import software.amazon.smithy.rust.codegen.core.smithy.typeNameContainsNonPublicType +import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index f0a09ec5bc..e6f7ac6e71 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.client.smithy.PubCrateConstrainedShapeSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustType @@ -31,7 +30,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -42,7 +41,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput -import software.amazon.smithy.rust.codegen.core.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled +import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled 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 diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 3ecca95a14..c30cd29b59 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -24,7 +24,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt index 2ad90e4859..4fa4bc8560 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt @@ -1,9 +1,30 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext fun StructureShape.serverBuilderSymbol(codegenContext: ServerCodegenContext): Symbol = this.serverBuilderSymbol(codegenContext.symbolProvider, !codegenContext.settings.codegenConfig.publicConstrainedTypes) + +fun StructureShape.serverBuilderSymbol(symbolProvider: SymbolProvider, pubCrate: Boolean): Symbol { + val structureSymbol = symbolProvider.toSymbol(this) + val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) + + if (pubCrate) { + "_internal" + } else { + "" + } + val rustType = RustType.Opaque("Builder", "${structureSymbol.namespace}::$builderNamespace") + return Symbol.builder() + .rustType(rustType) + .name(rustType.name) + .namespace(rustType.namespace, "::") + .definitionFile(structureSymbol.definitionFile) + .build() +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt index 8012d2b8be..2812c59d74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerStructureConstrainedTraitImpl.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.serverBuilderSymbol class ServerStructureConstrainedTraitImpl( private val symbolProvider: RustSymbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 4b7b1327dd..e217fe6e42 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -125,7 +125,7 @@ class UnconstrainedCollectionGenerator( """ impl $constraintViolationName { pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { - self.1.as_validation_exception_field(path + "/" + &self.0) + self.1.as_validation_exception_field(format!("{}/{}", path, &self.0)) } } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 35b22fa888..22ee9a0ed4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -14,8 +14,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol class ServerRequestBindingGenerator( protocol: Protocol, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index 2e4056fa68..cef6bc44e1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -19,7 +19,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType +import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext class ServerResponseBindingGenerator( @@ -47,8 +47,13 @@ class ServerResponseBindingGenerator( httpBindingGenerator.generateAddHeadersFn(shape, HttpMessageType.RESPONSE) } -// TODO Docs -class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext) : HttpBindingCustomization() { +/** + * A customization to, just before we iterate over a _constrained_ map shape that is bound to HTTP headers via + * `@httpPrefixHeaders`, unwrap the wrapper newtype and take a shared reference to the actual `std::collections::HashMap` + * within it. + */ +class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext) : + HttpBindingCustomization() { override fun section(section: HttpBindingSection): Writable = when (section) { is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders -> writable { if (workingWithPublicConstrainedWrapperTupleType(section.shape, codegenContext.model, codegenContext.settings.codegenConfig.publicConstrainedTypes)) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index aecaf3ed51..92908cdd90 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonS import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.customizations.BeforeIteratingOverMapJsonCustomization import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerAwsJsonProtocol import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol @@ -89,6 +90,6 @@ class ServerAwsJsonSerializerGenerator( codegenContext, httpBindingResolver, ::awsJsonFieldName, - customizations = listOf(ServerAwsJsonError(awsJsonVersion), ServerBeforeIteratingOverMapCustomization(codegenContext)), + customizations = listOf(ServerAwsJsonError(awsJsonVersion), BeforeIteratingOverMapJsonCustomization(codegenContext)), ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index ee4c43d69e..a913b806d2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,20 +5,15 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator -import software.amazon.smithy.rust.codegen.core.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.customizations.BeforeIteratingOverMapJsonCustomization import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol /** @@ -55,24 +50,6 @@ class ServerRestJsonSerializerGenerator( codegenContext, httpBindingResolver, ::restJsonFieldName, - customizations = listOf(ServerBeforeIteratingOverMapCustomization(codegenContext)), + customizations = listOf(BeforeIteratingOverMapJsonCustomization(codegenContext)), ), ) : StructuredDataSerializerGenerator by jsonSerializerGenerator - -// TODO Docs. -// TODO Move this into its own file, since `ServerAwsJson` also uses it. -class ServerBeforeIteratingOverMapCustomization(private val codegenContext: ServerCodegenContext) : JsonCustomization() { - override fun section(section: JsonSection): Writable = when (section) { - is JsonSection.ServerError -> emptySection - is JsonSection.BeforeIteratingOverMap -> writable { - if (workingWithPublicConstrainedWrapperTupleType( - section.shape, - codegenContext.model, - codegenContext.settings.codegenConfig.publicConstrainedTypes, - ) - ) { - rust("""let ${section.valueExpression.name} = &${section.valueExpression.name}.0;""") - } - } - } -} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 3f13478667..097b4e21b7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.client.smithy.transformers.ShapesReachableFromOperationInputTagger +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig @@ -120,15 +120,3 @@ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: Ru modelBuilder.renderConvenienceMethod(this) } } - -// TODO Inline -fun serverTestModelBaselineTransform(model: Model) = - model -// .let(RecursiveShapeBoxer::transform) -// // Normalize operations by adding synthetic input and output shapes to every operation -// .let(OperationNormalizer::transform) -// // TODO Docs. -// .let(RemoveEbsModelValidationException::transform) -// // TODO Docs. -// .let(AttachValidationExceptionToConstrainedOperationInputsInAllowList::transform) - .let(ShapesReachableFromOperationInputTagger::transform) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt similarity index 64% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 53a14a6cee..813151bcad 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy.transformers +package software.amazon.smithy.rust.codegen.server.smithy.transformers import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker @@ -12,12 +12,23 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.hasConstraintTrait +import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.core.util.inputShape /** - * TODO Docs - * TODO Move to server + * Attach the `smithy.framework#ValidationException` error to operations whose inputs are constrained, if they belong + * to a service in an allowlist. + * + * Some of the models we generate in CI have constrained operation inputs, but the operations don't have + * `smithy.framework#ValidationException` in their list of errors. This is a codegen error, unless + * `disableDefaultValidation` is set to `true`, a code generation mode we don't support yet. See [1] for more details. + * Until we implement said mode, we manually attach the error to build these models, since we don't own them (they're + * either actual AWS service model excerpts, or they come from the `awslabs/smithy` library. + * + * [1]: https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809424783 + * + * TODO(https://github.com/awslabs/smithy-rs/issues/1401): This transformer will go away once we implement + * `disableDefaultValidation` set to `true`, allowing service owners to map from constraint violations to operation errors. */ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { private val sherviceShapeIdAllowList = @@ -30,9 +41,6 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { ) fun transform(model: Model): Model { -// model.serviceShapes.filter { it.toShapeId().name == "RestJsonValidation" }[0].operations.map { model.expectShape(it, OperationShape::class.java) }.map { it.errors.add( -// ShapeId.from("smithy.framework#ValidationException")) } - val walker = Walker(model) val operationsWithConstrainedInputWithoutValidationException = model.serviceShapes @@ -45,17 +53,6 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { .any { it is SetShape || it is EnumShape || it.hasConstraintTrait() } } .filter { !it.errors.contains(ShapeId.from("smithy.framework#ValidationException")) } -// .map { it.errors } -// .forEach { -// it.errors.add(ShapeId.from("smithy.framework#ValidationException")) -// } -// val inputShapes = model.operationShapes.map { -// model.expectShape(it.inputShape, StructureShape::class.java) -// } -// val walker = Walker(model) -// val shapesReachableFromOperationInputs = inputShapes -// .flatMap { walker.walkShapes(it) } -// .toSet() return ModelTransformer.create().mapShapes(model) { shape -> if (shape is OperationShape && operationsWithConstrainedInputWithoutValidationException.contains(shape)) { @@ -64,9 +61,5 @@ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { shape } } -// for (errors in errorsInOperationsWithConstrainedInput) { -// errors.add(ShapeId.from("smithy.framework#ValidationException")) -// } -// return model } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RemoveEbsModelValidationException.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RemoveEbsModelValidationException.kt new file mode 100644 index 0000000000..d4c6feaed6 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/RemoveEbsModelValidationException.kt @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.transformers + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.core.util.orNull + +/** + * The Amazon Elastic Block Store (Amazon EBS) model is one model that we generate in CI. + * Unfortunately, it defines its own `ValidationException` shape, that conflicts with + * `smithy.framework#ValidationException` [0]. + * + * So this is a model that a service owner would generate when "disabling default validation": in such a code generation + * mode, the service owner is responsible for mapping an operation input-level constraint violation into a modeled + * operation error. This mode, as well as what the end goal for validation exception responses looks like, is described + * in more detail in [1]. We don't support this mode yet. + * + * So this transformer simply removes the EBB model's `ValidationException`. A subsequent model transformer, + * [AttachValidationExceptionToConstrainedOperationInputsInAllowList], ensures that it is replaced by + * `smithy.framework#ValidationException`. + * + * [0]: https://github.com/awslabs/smithy-rs/blob/274adf155042cde49251a0e6b8842d6f56cd5b6d/codegen-core/common-test-models/ebs.json#L1270-L1288 + * [1]: https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809424783 + * + * TODO(https://github.com/awslabs/smithy-rs/issues/1401): This transformer will go away once we implement + * `disableDefaultValidation` set to `true`, allowing service owners to map from constraint violations to operation errors. + */ +object RemoveEbsModelValidationException { + fun transform(model: Model): Model { + val shapeToRemove = model.getShape(ShapeId.from("com.amazonaws.ebs#ValidationException")).orNull() + return ModelTransformer.create().removeShapes(model, listOfNotNull(shapeToRemove)) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt similarity index 97% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt index 7f3188f122..9d63fb74fb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/ShapesReachableFromOperationInputTagger.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy.transformers +package software.amazon.smithy.rust.codegen.server.smithy.transformers import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker @@ -37,8 +37,6 @@ import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE * * [simple shapes]: https://awslabs.github.io/smithy/2.0/spec/simple-types.html * [aggregate shapes]: https://awslabs.github.io/smithy/2.0/spec/aggregate-types.html#aggregate-types - * - * TODO Move this to `server`. */ object ShapesReachableFromOperationInputTagger { fun transform(model: Model): Model { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index c1643900b3..80e2d93dae 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -13,8 +13,6 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 20946494f4..7f38763a5d 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -26,7 +26,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestModelBaselineTransform +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import java.util.stream.Stream class ConstrainedMapGeneratorTest { @@ -57,7 +57,7 @@ class ConstrainedMapGeneratorTest { key: String, value: String } - """.asSmithyModel().let { serverTestModelBaselineTransform(it) }, + """.asSmithyModel().let(ShapesReachableFromOperationInputTagger::transform), validMap, invalidMap, ) @@ -151,7 +151,7 @@ class ConstrainedMapGeneratorTest { key: String, value: String } - """.asSmithyModel().let { serverTestModelBaselineTransform(it) } + """.asSmithyModel().let(ShapesReachableFromOperationInputTagger::transform) val constrainedMapShape = model.lookup("test#ConstrainedMap") val writer = RustWriter.forModule(ModelsModule.name) From 8ada4a07d8e358ab79e66ee58bd313bda00cd934 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 15:25:01 +0200 Subject: [PATCH 231/255] ./gradlew ktlintFormat --- .../rust/codegen/server/smithy/ServerCodegenVisitor.kt | 6 +++--- .../BeforeIteratingOverMapJsonCustomization.kt | 2 +- .../generators/PubCrateConstrainedCollectionGenerator.kt | 2 +- .../smithy/generators/PubCrateConstrainedMapGenerator.kt | 2 +- .../server/smithy/generators/ServerBuilderGenerator.kt | 4 ++-- .../generators/http/ServerResponseBindingGenerator.kt | 2 +- .../codegen/server/smithy/testutil/ServerTestHelpers.kt | 1 - ...ationExceptionToConstrainedOperationInputsInAllowList.kt | 2 +- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index d164f28074..695fdb48f1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -24,9 +24,6 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.server.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList -import software.amazon.smithy.rust.codegen.server.smithy.transformers.RemoveEbsModelValidationException -import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget @@ -67,6 +64,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList +import software.amazon.smithy.rust.codegen.server.smithy.transformers.RemoveEbsModelValidationException +import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import java.util.logging.Logger /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt index 62fca7d9e7..5361ba3929 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt @@ -5,8 +5,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection -import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType /** * A customization to, just before we iterate over a _constrained_ map shape in a JSON serializer, unwrap the wrapper diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 9373cbecb2..d12c50d778 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -14,9 +14,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * A generator for a wrapper tuple newtype over a collection shape's symbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 262e8790f8..2c2aba56ec 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -15,9 +15,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType -import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * A generator for a wrapper tuple newtype over a map shape's symbol type. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index e6f7ac6e71..ffd1c03296 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -30,7 +30,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -41,12 +40,13 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput -import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled 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 import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled /** * Generates a builder for the Rust type associated with the [StructureShape]. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index cef6bc44e1..3a0a36a3ba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -19,8 +19,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType class ServerResponseBindingGenerator( protocol: Protocol, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 097b4e21b7..21f407dcb6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 813151bcad..67474d3039 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -12,8 +12,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.SetShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTrait import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTrait /** * Attach the `smithy.framework#ValidationException` error to operations whose inputs are constrained, if they belong From d709b58c491b24f74a3a45226b35bba5eafc2e6b Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 16:14:52 +0200 Subject: [PATCH 232/255] Fix more TODOs --- .../rust/codegen/core/smithy/generators/BuilderGenerator.kt | 3 ++- .../rust/codegen/core/smithy/generators/StructureGenerator.kt | 1 - .../core/smithy/generators/http/HttpBindingGenerator.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 17c3cfc3a3..386cb69a5d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -35,7 +35,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO Move this entire file to `codegen-client`. +// TODO This builder generator is only used by the client. +// Move this entire file, and its tests, to `codegen-client`. It's not as easy as it seems. fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { val structureSymbol = symbolProvider.toSymbol(this) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index b8e6dd362d..f0e1f98392 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -46,7 +46,6 @@ fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, } } -// TODO Is this used? fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): String { return if (member.getMemberTrait(model, SensitiveTrait::class.java).isPresent) { "*** Sensitive Data Redacted ***".dq() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 216f99d1da..77d551e128 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -78,7 +78,7 @@ enum class HttpMessageType { * Class describing an HTTP binding (de)serialization section that can be used in a customization. */ sealed class HttpBindingSection(name: String) : Section(name) { - // TODO `shape` should be `MapShape`. + // TODO `shape` should be `MapShape`. See the instantiator site, which is working with a `Shape` instead (and should not be). data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: Shape) : HttpBindingSection("BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders") } From f99ece847e028fecff023a80d955aa9640f58572 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 11 Oct 2022 17:32:57 +0200 Subject: [PATCH 233/255] Fix more TODOs --- .../http/RequestBindingGenerator.kt | 2 +- .../http/ResponseBindingGenerator.kt | 2 +- .../serialize/JsonSerializerGenerator.kt | 18 -------- ...hapeReachableFromOperationInputTagTrait.kt | 1 + .../generators/ConstrainedStringGenerator.kt | 6 ++- .../MapConstraintViolationGenerator.kt | 7 ++- .../ServerBuilderConstraintViolations.kt | 3 +- .../generators/ServerBuilderGenerator.kt | 10 ++--- .../smithy/generators/ServerEnumGenerator.kt | 8 ++-- .../generators/UnconstrainedMapGenerator.kt | 44 ++++++++++++++++--- .../generators/UnconstrainedUnionGenerator.kt | 6 ++- .../server/smithy/ServerInstantiatorTest.kt | 34 -------------- 12 files changed, 67 insertions(+), 74 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt index f1074c6cf4..721ddb1e19 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt @@ -48,7 +48,7 @@ fun SmithyPattern.rustFormatString(prefix: String, separator: String): String { return base.dq() } -// TODO Move to `codegen-client` and update docs +// TODO Move to `codegen-client` and update docs. `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. /** * Generates methods to serialize and deserialize requests based on the HTTP trait. Specifically: * 1. `fn update_http_request(builder: http::request::Builder) -> Builder` diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt index fbb961e545..4c448cda1a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -// TODO Move to `codegen-client` and update docs +// TODO Move to `codegen-client` and update docs. `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. class ResponseBindingGenerator( protocol: Protocol, private val codegenContext: CodegenContext, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index ae0a4b2ed1..3a14ace8ce 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -57,7 +57,6 @@ sealed class JsonSection(name: String) : Section(name) { /** Mutate the server error object prior to finalization. Eg: this can be used to inject `__type` to record the error type. */ data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSection("ServerError") - // TODO This could take in directly the `Context` data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSection("BeforeIteratingOverMap") } @@ -350,14 +349,6 @@ class JsonSerializerGenerator( private fun RustWriter.serializeMemberValue(context: MemberContext, target: Shape) { val writer = context.writerExpression - -// // TODO Use customization -// // let -// val value = if (workingWithPublicConstrainedWrapperTupleType(context.shape, codegenContext)) { -// ValueExpression.Value("${context.valueExpression.name}.0") -// } else { -// context.valueExpression -// } val value = context.valueExpression when (target) { @@ -434,15 +425,6 @@ class JsonSerializerGenerator( val valueName = safeName("value") customizations.forEach { customization -> customization.section(JsonSection.BeforeIteratingOverMap(context.shape, context.valueExpression))(this) } rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { - val keyTarget = model.expectShape(context.shape.key.target) - // TODO Remove -// val keyExpression = if (workingWithPublicConstrainedWrapperTupleType(keyTarget, codegenContext)) { -// "$keyName.0.as_str()" -// } else if (keyTarget.hasTrait()) { -// "$keyName.as_str()" -// } else { -// keyName -// } val keyExpression = "$keyName.as_str()" serializeMember(MemberContext.mapMember(context, keyExpression, valueName)) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index c650771ab7..ca1eacba22 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -30,6 +30,7 @@ class ShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.obje private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { is StructureShape, is UnionShape, is ListShape, is MapShape -> { + // TODO There are a bunch of sites where we're performing this check inline instead of calling this function. shape.hasTrait() } else -> PANIC("this method does not support shape type ${shape.type}") } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 528c540c4e..6ec2d527a2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -165,7 +166,10 @@ class ConstrainedStringGenerator( if (shape.hasTrait()) { rustBlock("impl ${constraintViolation.name}") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlockTemplate( + "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", + "String" to RuntimeType.String, + ) { rustBlock("match self") { rust( """ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 4ca0d5e6d4..ba251fc575 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -13,7 +13,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -82,7 +84,10 @@ class MapConstraintViolationGenerator( if (shape.hasTrait()) { rustBlock("impl $constraintViolationName") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlockTemplate( + "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", + "String" to RuntimeType.String, + ) { rustBlock("match self") { shape.getTrait()?.also { rust( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index c14f41e924..11be4549a1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -165,12 +165,13 @@ class ServerBuilderConstraintViolations( writer.rustTemplate( """ impl ConstraintViolation { - pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField { + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { #{ValidationExceptionFieldWritable:W} } } """, "ValidationExceptionFieldWritable" to validationExceptionFieldWritable, + "String" to RuntimeType.String, ) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index ffd1c03296..29f0e2cb7f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -119,18 +119,16 @@ class ServerBuilderGenerator( private fun renderBuilder(writer: RustWriter) { if (isBuilderFallible) { - // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is - // an operation input shape. - val shouldConvertConstraintViolationToRequestRejection = shape.hasTrait() - serverBuilderConstraintViolations.render( writer, visibility, nonExhaustive = true, - shouldRenderAsValidationExceptionFieldList = shouldConvertConstraintViolationToRequestRejection, + shouldRenderAsValidationExceptionFieldList = shape.isReachableFromOperationInput(), ) - if (shouldConvertConstraintViolationToRequestRejection) { + // Only generate converter from `ConstraintViolation` into `RequestRejection` if the structure shape is + // an operation input shape. + if (shape.hasTrait()) { renderImplFromConstraintViolationForRequestRejection(writer) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index fa6ac30f3b..46eb4fe48c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -46,7 +46,6 @@ open class ServerEnumGenerator( writer.withModule( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), ) { - // TODO Check that we're using `#{String}` in the other as_validation_exception_field methods. rustTemplate( """ ##[derive(Debug, PartialEq)] @@ -55,12 +54,11 @@ open class ServerEnumGenerator( *codegenScope, ) - // TODO Move out. - val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") - val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" - // TODO ValidationException should live under `crate::error::`. if (shape.hasTrait()) { + val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") + val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" + rustBlock("impl $constraintViolationName") { rustBlockTemplate("pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { rust( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index c303ac647e..e3f682b2a0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -10,9 +10,12 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.join +import software.amazon.smithy.rust.codegen.core.rustlang.plus import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -105,21 +108,52 @@ class UnconstrainedMapGenerator( constrainedShapeSymbolProvider.toSymbol(valueShape) } - // TODO Refactor to use `return` so that we don't have to clone `k`. + val constrainedKeySymbol = constrainedShapeSymbolProvider.toSymbol(keyShape) + val constrainKeyWritable = writable { + rustTemplate( + "let k: #{ConstrainedKeySymbol} = k.try_into().map_err(Self::Error::Key)?;", + "ConstrainedKeySymbol" to constrainedKeySymbol, + ) + } + val constrainValueWritable = writable { + rustTemplate( + """ + match #{ConstrainedValueSymbol}::try_from(v) { + Ok(v) => Ok((k, v)), + Err(inner_constraint_violation) => Err(Self::Error::Value(k, inner_constraint_violation)), + } + """, + "ConstrainedValueSymbol" to constrainedValueSymbol, + ) + } + val epilogueWritable = writable { rust("Ok((k, v))") } + + val constrainKVWritable = if ( + isKeyConstrained(keyShape, symbolProvider) && + isValueConstrained(valueShape, model, symbolProvider) + ) { + listOf(constrainKeyWritable, constrainValueWritable).join("\n") + } else if (isKeyConstrained(keyShape, symbolProvider)) { + listOf(constrainKeyWritable, epilogueWritable).join("\n") + } else if (isValueConstrained(valueShape, model, symbolProvider)) { + constrainValueWritable + } else { + epilogueWritable + } + rustTemplate( """ let res: Result, Self::Error> = value.0 .into_iter() .map(|(k, v)| { - ${if (isKeyConstrained(keyShape, symbolProvider)) "let k: #{ConstrainedKeySymbol} = k.try_into().map_err(Self::Error::Key)?;" else ""} - ${if (isValueConstrained(valueShape, model, symbolProvider)) "let v: #{ConstrainedValueSymbol} = v.try_into().map_err(|inner_violation| Self::Error::Value(k.clone(), inner_violation))?;" else ""} - Ok((k, v)) + #{ConstrainKVWritable:W} }) .collect(); let hm = res?; """, - "ConstrainedKeySymbol" to constrainedShapeSymbolProvider.toSymbol(keyShape), + "ConstrainedKeySymbol" to constrainedKeySymbol, "ConstrainedValueSymbol" to constrainedValueSymbol, + "ConstrainKVWritable" to constrainKVWritable, ) val constrainedValueTypeIsNotFinalType = diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 85cbde1228..2fdc47b9c8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate @@ -142,7 +143,10 @@ class UnconstrainedUnionGenerator( if (shape.hasTrait()) { rustBlock("impl $constraintViolationName") { - rustBlock("pub(crate) fn as_validation_exception_field(self, path: String) -> crate::model::ValidationExceptionField") { + rustBlockTemplate( + "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", + "String" to RuntimeType.String, + ) { withBlock("match self {", "}") { for (constraintViolation in constraintViolations()) { rust("""Self::${constraintViolation.name()}(inner) => inner.as_validation_exception_field(path + "/${constraintViolation.forMember.memberName}"),""") diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt index 550231b94c..1802e76819 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerInstantiatorTest.kt @@ -172,39 +172,5 @@ class ServerInstantiatorTest { } } project.compileAndTest() - - // TODO Remove -// writer.test { -// writer.withBlock("let result = ", ";") { -// sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) -// } -// writer.write( -// """ -// use std::collections::HashMap; -// use aws_smithy_types::{DateTime, Document}; -// -// let expected = MyStructRequired { -// str: "".to_owned(), -// primitive_int: 0, -// int: 0, -// ts: DateTime::from_secs(0), -// byte: 0, -// union: NestedUnion::Struct(NestedStruct { -// str: "".into(), -// num: 0, -// }), -// structure: NestedStruct { -// str: "".into(), -// num: 0, -// }, -// list: Vec::new(), -// map: HashMap::new(), -// doc: Document::Object(HashMap::new()), -// }; -// assert_eq!(result, expected); -// """, -// ) -// } -// writer.compileAndTest() } } From ad3fb6ae47afc8c384a28f87cd14a465f12307fe Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 12 Oct 2022 00:17:20 +0200 Subject: [PATCH 234/255] Fix more TODOs --- ...hapeReachableFromOperationInputTagTrait.kt | 5 +++-- .../generators/ConstrainedStringGenerator.kt | 5 ++--- .../MapConstraintViolationGenerator.kt | 4 ++-- .../smithy/generators/ServerEnumGenerator.kt | 22 +++++++++---------- .../UnconstrainedCollectionGenerator.kt | 9 ++++---- .../generators/UnconstrainedUnionGenerator.kt | 4 ++-- .../UnconstrainedCollectionGeneratorTest.kt | 5 ++--- .../aws-smithy-http-server/src/rejection.rs | 6 ----- 8 files changed, 25 insertions(+), 35 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index ca1eacba22..331eceb37b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ListShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.AnnotationTrait @@ -29,12 +30,12 @@ class ShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.obje } private fun isShapeReachableFromOperationInput(shape: Shape) = when (shape) { - is StructureShape, is UnionShape, is ListShape, is MapShape -> { - // TODO There are a bunch of sites where we're performing this check inline instead of calling this function. + is StructureShape, is UnionShape, is MapShape, is ListShape, is StringShape -> { shape.hasTrait() } else -> PANIC("this method does not support shape type ${shape.type}") } +fun StringShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) fun StructureShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) fun CollectionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) fun UnionShape.isReachableFromOperationInput() = isShapeReachableFromOperationInput(this) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 6ec2d527a2..0c402caad5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -20,9 +20,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.expectTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage @@ -164,7 +163,7 @@ class ConstrainedStringGenerator( """, ) - if (shape.hasTrait()) { + if (shape.isReachableFromOperationInput()) { rustBlock("impl ${constraintViolation.name}") { rustBlockTemplate( "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index ba251fc575..4082baeede 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider @@ -82,7 +82,7 @@ class MapConstraintViolationGenerator( *constraintViolationCodegenScope, ) - if (shape.hasTrait()) { + if (shape.isReachableFromOperationInput()) { rustBlock("impl $constraintViolationName") { rustBlockTemplate( "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index 46eb4fe48c..bc3e04f477 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -8,15 +8,13 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -54,23 +52,23 @@ open class ServerEnumGenerator( *codegenScope, ) - // TODO ValidationException should live under `crate::error::`. - if (shape.hasTrait()) { + if (shape.isReachableFromOperationInput()) { val enumValueSet = enumTrait.enumDefinitionValues.joinToString(", ") val message = "Value {} at '{}' failed to satisfy constraint: Member must satisfy enum value set: [$enumValueSet]" - rustBlock("impl $constraintViolationName") { - rustBlockTemplate("pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", *codegenScope) { - rust( - """ + rustTemplate( + """ + impl $constraintViolationName { + pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField { crate::model::ValidationExceptionField { message: format!(r##"$message"##, &self.0, &path), path, } - """, - ) + } } - } + """, + *codegenScope, + ) } } writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.TryFrom) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index e217fe6e42..8565fa1db0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -13,8 +13,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -107,8 +106,8 @@ class UnconstrainedCollectionGenerator( constraintViolationSymbol.namespace.split(constraintViolationSymbol.namespaceDelimiter).last(), RustMetadata(visibility = constraintViolationVisibility), ) { - // TODO We only need to generate `usize` when the collection shape is directly constrained: see - // UnconstrainedCollectionGeneratorTest for an example where it isn't. + // The first component of the tuple struct is the index in the collection where the first constraint + // violation was found. rustTemplate( """ ##[derive(Debug, PartialEq)] @@ -120,7 +119,7 @@ class UnconstrainedCollectionGenerator( "InnerConstraintViolationSymbol" to innerConstraintViolationSymbol, ) - if (shape.hasTrait()) { + if (shape.isReachableFromOperationInput()) { rustTemplate( """ impl $constraintViolationName { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 2fdc47b9c8..f5e4ddf759 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -27,7 +27,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput 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.toPascalCase @@ -141,7 +141,7 @@ class UnconstrainedUnionGenerator( constraintViolations().forEach { renderConstraintViolation(this, it) } } - if (shape.hasTrait()) { + if (shape.isReachableFromOperationInput()) { rustBlock("impl $constraintViolationName") { rustBlockTemplate( "pub(crate) fn as_validation_exception_field(self, path: #{String}) -> crate::model::ValidationExceptionField", diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index d813471542..749780fec5 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -64,12 +64,11 @@ class UnconstrainedCollectionGeneratorTest { UnconstrainedCollectionGenerator(codegenContext, unconstrainedModuleWriter, modelsModuleWriter, it).render() } - // TODO We should not have to pass in 0. See TODO in `UnconstrainedCollectionGenerator`. unconstrainedModuleWriter.unitTest( name = "list_a_unconstrained_fail_to_constrain_with_first_error", test = """ let c_builder1 = crate::model::StructureC::builder().int(69); - let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); + let c_builder2 = crate::model::StructureC::builder().string("david".to_owned()); let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); @@ -93,7 +92,7 @@ class UnconstrainedCollectionGeneratorTest { let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected: Vec> = vec![vec![crate::model::StructureC { - string: String::from("david"), + string: "david".to_owned(), int: 69 }]]; let actual: Vec> = diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index a06b4e1d03..65d4497ffe 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -185,12 +185,6 @@ pub enum RequestRejection { // TODO(https://github.com/awslabs/smithy-rs/issues/1703): this will hold a type that can be // rendered into a protocol-specific response later on. ConstraintViolation(String), - - // TODO ConstraintViolation should supersede this variant. - /// Used by the server when the enum variant sent by a client is not known. - // Unlike the rejections above, the inner type is code generated, - // with each enum having its own generated error type. - EnumVariantNotFound(crate::error::BoxError), } #[derive(Debug, Display)] From abd003b88229430883db7ea09b2a36449bb63508 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 12 Oct 2022 02:02:33 +0200 Subject: [PATCH 235/255] Adjust two TODOs --- .../codegen/core/smithy/generators/http/HttpBindingGenerator.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 77d551e128..a08762a40e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -79,6 +79,7 @@ enum class HttpMessageType { */ sealed class HttpBindingSection(name: String) : Section(name) { // TODO `shape` should be `MapShape`. See the instantiator site, which is working with a `Shape` instead (and should not be). + // Addressed in https://github.com/awslabs/smithy-rs/pull/1841 data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: Shape) : HttpBindingSection("BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders") } @@ -566,6 +567,7 @@ class HttpBindingGenerator( } ifSet(memberType, memberSymbol, "&input.$memberName") { field -> // TODO How can this be a collection shape? Makes no sense, `httpPrefixHeaders` is for map shapes. + // Addressed in https://github.com/awslabs/smithy-rs/pull/1841 val listHeader = memberType is CollectionShape customizations.forEach { customization -> customization.section( From fc116adf07534f8344ebe90ae4c7351a7f6fe552 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 14 Oct 2022 11:15:12 +0200 Subject: [PATCH 236/255] Add TODOs from first PR review --- codegen-client-test/model/constraints.smithy | 1 - .../codegen/client/smithy/customize/RustCodegenDecorator.kt | 1 + codegen-core/common-test-models/constraints.smithy | 1 + .../common-test-models/smithy.framework.validation.smithy | 2 ++ .../smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt | 1 + .../smithy/traits/ShapeReachableFromOperationInputTagTrait.kt | 2 ++ 6 files changed, 7 insertions(+), 1 deletion(-) delete mode 120000 codegen-client-test/model/constraints.smithy diff --git a/codegen-client-test/model/constraints.smithy b/codegen-client-test/model/constraints.smithy deleted file mode 120000 index 08b77ecb24..0000000000 --- a/codegen-client-test/model/constraints.smithy +++ /dev/null @@ -1 +0,0 @@ -../../codegen-server-test/model/constraints.smithy \ No newline at end of file diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RustCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RustCodegenDecorator.kt index 29e7c2c1ca..bfaae9c871 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RustCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RustCodegenDecorator.kt @@ -44,6 +44,7 @@ interface RustCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + // This is only used by decorators for smithy-rs _clients_. fun operationCustomizations( codegenContext: C, operation: OperationShape, diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index e87e1fb1ae..182207bb20 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -10,6 +10,7 @@ use smithy.framework#ValidationException @title("ConstraintsService") service ConstraintsService { operations: [ + // TODO Rename as {Verb}[{Qualifier}]{Noun}: https://github.com/awslabs/smithy-rs/pull/1342#discussion_r980936650 ConstrainedShapesOperation, ConstrainedHttpBoundShapesOperation, ConstrainedRecursiveShapesOperation, diff --git a/codegen-core/common-test-models/smithy.framework.validation.smithy b/codegen-core/common-test-models/smithy.framework.validation.smithy index 26532c655e..156b473ab3 100644 --- a/codegen-core/common-test-models/smithy.framework.validation.smithy +++ b/codegen-core/common-test-models/smithy.framework.validation.smithy @@ -1,6 +1,8 @@ // This file is a copy of // https://github.com/awslabs/smithy/blob/7b2a980c73e87af3214044fe9ca7c391fb269633/smithy-validation-model/model/smithy.framework.validation.smithy +// TODO This file should be in the classpath. + $version: "2.0" namespace smithy.framework diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 07c729b2f4..81dcd85558 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -329,6 +329,7 @@ class EventStreamUnmarshallerGenerator( } rust(header) for (member in syntheticUnion.errorMembers) { + // TODO `member.memberName` needs further investigation https://github.com/awslabs/smithy-rs/pull/1342#discussion_r983792584 rustBlock("${member.memberName.dq()} $matchOperator ") { // TODO(EventStream): Errors on the operation can be disjoint with errors in the union, // so we need to generate a new top-level Error type for each event stream union. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index 331eceb37b..f3e5bd331d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -18,6 +18,8 @@ import software.amazon.smithy.model.traits.AnnotationTrait import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.hasTrait +// TODO Move to server. + /** * Tag to indicate that an aggregate shape is reachable from operation input. * From 35da738f6cc5a26bf3344edd8509288c8484ba2f Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 14 Oct 2022 22:35:08 +0200 Subject: [PATCH 237/255] Refactor `Instantiator` The `Instantiator` is a rather old part of the codebase that has been accumulating a bit of technical debt. This commit refactors `Instantiator.kt`. * Removes dependency on `CodegenTarget`; the instantiator is now client and server agnostic. `ClientInstantiator` and `ServerInstantiator` instantiate the underlying instantiator with the settings needed by each project. * The test suite has been completely rewritten to make use of the newer testing infrastructure (`TestWorkspace.testProject()`), and coverage around enum generation has been improved. * `Instantiator.Ctx` and its uses have been simplified. * The modified code has been reformatted to adhere to our styling conventions. This commit also renames `StructureGenerator.fallibleBuilder` to `StructureGenerator.hasFallibleBuilder`. All in all this commit will make the diff of #1342 lighter, where deviations among clients and servers regarding structure instantiation substantially increase. --- .../smithy/generators/ClientInstantiator.kt | 25 ++ .../protocol/ProtocolTestGenerator.kt | 7 +- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../generators/ClientInstantiatorTest.kt | 87 +++++ .../smithy/generators/BuilderGenerator.kt | 2 +- .../core/smithy/generators/Instantiator.kt | 160 +++++---- .../smithy/generators/StructureGenerator.kt | 2 +- .../protocols/parse/JsonParserGenerator.kt | 2 +- .../parse/XmlBindingTraitParserGenerator.kt | 2 +- .../rust/codegen/core/testutil/TestHelpers.kt | 7 +- .../smithy/generators/InstantiatorTest.kt | 305 +++++++----------- .../smithy/generators/ServerInstantiator.kt | 29 ++ .../protocol/ServerProtocolTestGenerator.kt | 9 +- .../ServerHttpBoundProtocolGenerator.kt | 2 +- .../smithy/testutil/ServerTestHelpers.kt | 3 +- .../generators/ServerInstantiatorTest.kt | 221 +++++++++++++ 16 files changed, 578 insertions(+), 287 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt new file mode 100644 index 0000000000..71891bf10b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator + +private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writable { + rust("#T::from($data)", enumSymbol) +} + +class ClientInstantiator(val codegenContext: CodegenContext) : + Instantiator( + codegenContext.symbolProvider, + codegenContext.model, + codegenContext.runtimeConfig, + ::enumFromStringFn, + ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 4bf70a2f7c..bceeda5d4b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase import software.amazon.smithy.protocoltests.traits.HttpRequestTestsTrait import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata @@ -31,9 +32,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.testutil.TokioTest @@ -64,9 +63,7 @@ class ProtocolTestGenerator( private val operationSymbol = codegenContext.symbolProvider.toSymbol(operationShape) private val operationIndex = OperationIndex.of(codegenContext.model) - private val instantiator = with(codegenContext) { - Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - } + private val instantiator = ClientInstantiator(codegenContext) private val codegenScope = arrayOf( "SmithyHttp" to CargoDependency.SmithyHttp(codegenContext.runtimeConfig).asType(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index f461f957cb..0d2f1541fa 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -332,7 +332,7 @@ class HttpBoundProtocolTraitImplGenerator( } } - val err = if (StructureGenerator.fallibleBuilder(outputShape, symbolProvider)) { + val err = if (StructureGenerator.hasFallibleBuilder(outputShape, symbolProvider)) { ".map_err(${format(errorSymbol)}::unhandled)?" } else "" diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt new file mode 100644 index 0000000000..34a183aa4c --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.client.testutil.testCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.lookup + +internal class ClientInstantiatorTest { + private val model = """ + namespace com.test + + @enum([ + { value: "t2.nano" }, + { value: "t2.micro" }, + ]) + string UnnamedEnum + + @enum([ + { + value: "t2.nano", + name: "T2_NANO", + }, + { + value: "t2.micro", + name: "T2_MICRO", + }, + ]) + string NamedEnum + """.asSmithyModel() + + private val codegenContext = testCodegenContext(model) + private val symbolProvider = codegenContext.symbolProvider + + @Test + fun `generate named enums`() { + val shape = model.lookup("com.test#NamedEnum") + val sut = ClientInstantiator(codegenContext) + val data = Node.parse("t2.nano".dq()) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + EnumGenerator(model, symbolProvider, writer, shape, shape.expectTrait()).render() + writer.unitTest("generate_named_enums") { + withBlock("let result = ", ";") { + sut.render(this, shape, data) + } + rust("assert_eq!(result, NamedEnum::T2Nano);") + } + } + project.compileAndTest() + } + + @Test + fun `generate unnamed enums`() { + val shape = model.lookup("com.test#UnnamedEnum") + val sut = ClientInstantiator(codegenContext) + val data = Node.parse("t2.nano".dq()) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + EnumGenerator(model, symbolProvider, writer, shape, shape.expectTrait()).render() + writer.unitTest("generate_unnamed_enums") { + withBlock("let result = ", ";") { + sut.render(this, shape, data) + } + rust("""assert_eq!(result, UnnamedEnum("t2.nano".to_owned()));""") + } + } + project.compileAndTest() + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index c561b04c39..a82bec24ef 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -78,7 +78,7 @@ class BuilderGenerator( } private fun renderBuildFn(implBlockWriter: RustWriter) { - val fallibleBuilder = StructureGenerator.fallibleBuilder(shape, symbolProvider) + val fallibleBuilder = StructureGenerator.hasFallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index a869e73255..a58c0c65be 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.ArrayNode import software.amazon.smithy.model.node.Node @@ -29,9 +30,11 @@ import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait +import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asType import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -40,7 +43,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider @@ -49,57 +51,48 @@ import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.letIf /** - * Instantiator generates code to instantiate a given Shape given a `Node` representing the value + * Instantiator generates code to instantiate a given Shape given a `Node` representing the value. * - * This is primarily used during Protocol test generation + * This is only used during protocol test generation. */ -class Instantiator( +open class Instantiator( private val symbolProvider: RustSymbolProvider, private val model: Model, private val runtimeConfig: RuntimeConfig, - private val target: CodegenTarget, + /** + * A function that given a symbol for an enum shape and a string, returns a writable to instantiate the enum with + * the string value. + **/ + private val enumFromStringFn: (Symbol, String) -> Writable, + /** Fill out required fields with a default value **/ + private val defaultsForRequiredFields: Boolean = false, ) { data class Ctx( - // The Rust HTTP library lower cases headers but Smithy protocol tests - // contain httpPrefix headers with uppercase keys - val lowercaseMapKeys: Boolean, - val streaming: Boolean, - // Whether we are instantiating with a Builder, in which case all setters take Option - val builder: Boolean, - // Fill out `required` fields with a default value. - val defaultsForRequiredFields: Boolean, + // The `http` crate requires that headers be lowercase, but Smithy protocol tests + // contain headers with uppercase keys. + val lowercaseMapKeys: Boolean = false, ) - companion object { - fun defaultContext() = Ctx(lowercaseMapKeys = false, streaming = false, builder = false, defaultsForRequiredFields = false) - } - - fun render( - writer: RustWriter, - shape: Shape, - arg: Node, - ctx: Ctx = defaultContext(), - ) { + fun render(writer: RustWriter, shape: Shape, data: Node, ctx: Ctx = Ctx()) { when (shape) { // Compound Shapes - is StructureShape -> renderStructure(writer, shape, arg as ObjectNode, ctx.copy(builder = true)) - is UnionShape -> renderUnion(writer, shape, arg as ObjectNode, ctx) + is StructureShape -> renderStructure(writer, shape, data as ObjectNode, ctx) + is UnionShape -> renderUnion(writer, shape, data as ObjectNode, ctx) // Collections - is ListShape -> renderList(writer, shape, arg as ArrayNode, ctx) - is MapShape -> renderMap(writer, shape, arg as ObjectNode, ctx) - is SetShape -> renderSet(writer, shape, arg as ArrayNode, ctx) + is ListShape -> renderList(writer, shape, data as ArrayNode, ctx) + is MapShape -> renderMap(writer, shape, data as ObjectNode, ctx) + is SetShape -> renderSet(writer, shape, data as ArrayNode, ctx) // Members, supporting potentially optional members - is MemberShape -> renderMember(writer, shape, arg, ctx) + is MemberShape -> renderMember(writer, shape, data, ctx) // Wrapped Shapes - is TimestampShape -> writer.write( - "#T::from_secs(${(arg as NumberNode).value})", + is TimestampShape -> writer.rust( + "#T::from_secs(${(data as NumberNode).value})", RuntimeType.DateTime(runtimeConfig), ) @@ -108,38 +101,40 @@ class Instantiator( * Blob::new("arg") * ``` */ - is BlobShape -> if (ctx.streaming) { - writer.write( - "#T::from_static(b${(arg as StringNode).value.dq()})", + is BlobShape -> if (shape.hasTrait()) { + writer.rust( + "#T::from_static(b${(data as StringNode).value.dq()})", RuntimeType.ByteStream(runtimeConfig), ) } else { - writer.write( - "#T::new(${(arg as StringNode).value.dq()})", + writer.rust( + "#T::new(${(data as StringNode).value.dq()})", RuntimeType.Blob(runtimeConfig), ) } // Simple Shapes - is StringShape -> renderString(writer, shape, arg as StringNode) - is NumberShape -> when (arg) { + is StringShape -> renderString(writer, shape, data as StringNode) + is NumberShape -> when (data) { is StringNode -> { val numberSymbol = symbolProvider.toSymbol(shape) // support Smithy custom values, such as Infinity writer.rust( - """<#T as #T>::parse_smithy_primitive(${arg.value.dq()}).expect("invalid string for number")""", + """<#T as #T>::parse_smithy_primitive(${data.value.dq()}).expect("invalid string for number")""", numberSymbol, CargoDependency.SmithyTypes(runtimeConfig).asType().member("primitive::Parse"), ) } - is NumberNode -> writer.write(arg.value) + + is NumberNode -> writer.write(data.value) } - is BooleanShape -> writer.write(arg.asBooleanNode().get().toString()) + + is BooleanShape -> writer.rust(data.asBooleanNode().get().toString()) is DocumentShape -> writer.rustBlock("") { val smithyJson = CargoDependency.smithyJson(runtimeConfig).asType() rustTemplate( """ - let json_bytes = br##"${Node.prettyPrintJson(arg)}"##; + let json_bytes = br##"${Node.prettyPrintJson(data)}"##; let mut tokens = #{json_token_iter}(json_bytes).peekable(); #{expect_document}(&mut tokens).expect("well formed json") """, @@ -147,26 +142,30 @@ class Instantiator( "json_token_iter" to smithyJson.member("deserialize::json_token_iter"), ) } - else -> writer.writeWithNoFormatting("todo!() /* $shape $arg */") + + else -> writer.writeWithNoFormatting("todo!() /* $shape $data */") } } /** - * If the shape is optional: `Some(inner)` or `None` - * otherwise: `inner` + * If the shape is optional: `Some(inner)` or `None`. + * Otherwise: `inner`. */ - private fun renderMember(writer: RustWriter, shape: MemberShape, arg: Node, ctx: Ctx) { - val target = model.expectShape(shape.target) - val symbol = symbolProvider.toSymbol(shape) - if (arg is NullNode) { - check( - symbol.isOptional(), - ) { "A null node was provided for $shape but the symbol was not optional. This is invalid input data." } - writer.write("None") + private fun renderMember(writer: RustWriter, memberShape: MemberShape, data: Node, ctx: Ctx) { + val targetShape = model.expectShape(memberShape.target) + val symbol = symbolProvider.toSymbol(memberShape) + if (data is NullNode) { + check(symbol.isOptional()) { + "A null node was provided for $memberShape but the symbol was not optional. This is invalid input data." + } + writer.rust("None") } else { + // Structure builder setters for structure shape members _always_ take in `Option`. + // Other aggregate shapes' members are optional only when their symbol is. writer.conditionalBlock( - "Some(", ")", - conditional = ctx.builder || symbol.isOptional(), + "Some(", + ")", + conditional = model.expectShape(memberShape.container) is StructureShape || symbol.isOptional(), ) { writer.conditionalBlock( "Box::new(", @@ -175,13 +174,11 @@ class Instantiator( ) { render( this, - target, - arg, - ctx.copy(builder = false) - .letIf(shape.getMemberTrait(model, HttpPrefixHeadersTrait::class.java).isPresent) { + targetShape, + data, + ctx.copy() + .letIf(memberShape.hasTrait()) { it.copy(lowercaseMapKeys = true) - }.letIf(shape.isStreaming(model)) { - it.copy(streaming = true) }, ) } @@ -189,9 +186,7 @@ class Instantiator( } } - private fun renderSet(writer: RustWriter, shape: SetShape, data: ArrayNode, ctx: Ctx) { - renderList(writer, shape, data, ctx) - } + private fun renderSet(writer: RustWriter, shape: SetShape, data: ArrayNode, ctx: Ctx) = renderList(writer, shape, data, ctx) /** * ```rust @@ -201,13 +196,14 @@ class Instantiator( * ret.insert("k2", ...); * ret * } + * ``` */ private fun renderMap(writer: RustWriter, shape: MapShape, data: ObjectNode, ctx: Ctx) { if (data.members.isEmpty()) { - writer.write("#T::new()", RustType.HashMap.RuntimeType) + writer.rust("#T::new()", RustType.HashMap.RuntimeType) } else { writer.rustBlock("") { - write("let mut ret = #T::new();", RustType.HashMap.RuntimeType) + rust("let mut ret = #T::new();", RustType.HashMap.RuntimeType) for ((key, value) in data.members) { withBlock("ret.insert(", ");") { renderMember(this, shape.key, key, ctx) @@ -218,7 +214,7 @@ class Instantiator( renderMember(this, shape.value, value, ctx) } } - write("ret") + rust("ret") } } } @@ -231,7 +227,7 @@ class Instantiator( private fun renderUnion(writer: RustWriter, shape: UnionShape, data: ObjectNode, ctx: Ctx) { val unionSymbol = symbolProvider.toSymbol(shape) - val variant = if (ctx.defaultsForRequiredFields && data.members.isEmpty()) { + val variant = if (defaultsForRequiredFields && data.members.isEmpty()) { val (name, memberShape) = shape.allMembers.entries.first() val targetShape = model.expectShape(memberShape.target) Node.from(name) to fillDefaultValue(targetShape) @@ -243,8 +239,8 @@ class Instantiator( val memberName = variant.first.value val member = shape.expectMember(memberName) - writer.write("#T::${symbolProvider.toMemberName(member)}", unionSymbol) - // unions should specify exactly one member + writer.rust("#T::${symbolProvider.toMemberName(member)}", unionSymbol) + // Unions should specify exactly one member. writer.withBlock("(", ")") { renderMember(this, member, variant.second, ctx) } @@ -259,7 +255,7 @@ class Instantiator( writer.withBlock("vec![", "]") { data.elements.forEach { v -> renderMember(this, shape.member, v, ctx) - write(",") + rust(",") } } } @@ -267,14 +263,10 @@ class Instantiator( private fun renderString(writer: RustWriter, shape: StringShape, arg: StringNode) { val data = writer.escape(arg.value).dq() if (!shape.hasTrait()) { - writer.rust("$data.to_string()") + writer.rust("$data.to_owned()") } else { val enumSymbol = symbolProvider.toSymbol(shape) - if (target == CodegenTarget.SERVER) { - writer.rust("""#T::try_from($data).expect("This is used in tests ONLY")""", enumSymbol) - } else { - writer.rust("#T::from($data)", enumSymbol) - } + writer.rustTemplate("#{EnumFromStringFn:W}", "EnumFromStringFn" to enumFromStringFn(enumSymbol, data)) } } @@ -290,11 +282,11 @@ class Instantiator( } } - writer.write("#T::builder()", symbolProvider.toSymbol(shape)) - if (ctx.defaultsForRequiredFields) { + writer.rust("#T::builder()", symbolProvider.toSymbol(shape)) + if (defaultsForRequiredFields) { shape.allMembers.entries .filter { (name, memberShape) -> - memberShape.isRequired && !data.members.containsKey(Node.from(name)) + !symbolProvider.toSymbol(memberShape).isOptional() && !data.members.containsKey(Node.from(name)) } .forEach { (_, memberShape) -> renderMemberHelper(memberShape, fillDefaultValue(memberShape)) @@ -305,9 +297,9 @@ class Instantiator( val memberShape = shape.expectMember(key.value) renderMemberHelper(memberShape, value) } - writer.write(".build()") - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { - writer.write(".unwrap()") + writer.rust(".build()") + if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { + writer.rust(".unwrap()") } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 96f508c5d0..d3ea548c4c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -69,7 +69,7 @@ open class StructureGenerator( companion object { /** Returns whether a structure shape requires a fallible builder to be generated. */ - fun fallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = + fun hasFallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = // All operation inputs should have fallible builders in case a new required field is added in the future. structureShape.hasTrait() || structureShape diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 5169242add..f74a4d60b9 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -411,7 +411,7 @@ class JsonParserGenerator( rustTemplate("let mut builder = #{Shape}::builder();", *codegenScope, "Shape" to symbol) deserializeStructInner(shape.members()) withBlock("Ok(Some(builder.build()", "))") { - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { + if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { rustTemplate( """.map_err(|err| #{Error}::new( #{ErrorReason}::Custom(format!("{}", err).into()), None) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 92ab1a4971..a8bcf48c19 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -475,7 +475,7 @@ class XmlBindingTraitParserGenerator( rust("let _ = decoder;") } withBlock("Ok(builder.build()", ")") { - if (StructureGenerator.fallibleBuilder(shape, symbolProvider)) { + if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { // NOTE:(rcoh) This branch is unreachable given the current nullability rules. // Only synthetic inputs can have fallible builders, but synthetic inputs can never be parsed // (because they're inputs, only outputs will be parsed!) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt index 848762ff18..f5f08e9076 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt @@ -101,7 +101,12 @@ internal fun testCodegenContext( /** * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ -fun StructureShape.renderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter, forWhom: CodegenTarget = CodegenTarget.CLIENT) { +fun StructureShape.renderWithModelBuilder( + model: Model, + symbolProvider: RustSymbolProvider, + writer: RustWriter, + forWhom: CodegenTarget = CodegenTarget.CLIENT, +) { StructureGenerator(model, symbolProvider, writer, this).render(forWhom) val modelBuilder = BuilderGenerator(model, symbolProvider, this) modelBuilder.render(writer) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt index 9bf232cc60..91d6045c0c 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt @@ -6,30 +6,32 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import org.junit.jupiter.api.Test +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.rustlang.raw +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.lookup class InstantiatorTest { private val model = """ namespace com.test + @documentation("this documents the shape") structure MyStruct { foo: String, @@ -68,29 +70,6 @@ class InstantiatorTest { value: Integer } - structure MyStructRequired { - @required - str: String, - @required - primitiveInt: PrimitiveInteger, - @required - int: Integer, - @required - ts: Timestamp, - @required - byte: Byte - @required - union: NestedUnion, - @required - structure: NestedStruct, - @required - list: MyList, - @required - map: NestedMap, - @required - doc: Document - } - union NestedUnion { struct: NestedStruct, int: Integer @@ -107,216 +86,176 @@ class InstantiatorTest { private val symbolProvider = testSymbolProvider(model) private val runtimeConfig = TestRuntimeConfig - fun RustWriter.test(block: RustWriter.() -> Unit) { - raw("#[test]") - rustBlock("fn inst()") { - block(this) - } - } + private fun enumFromStringFn(symbol: Symbol, data: String) = writable { } @Test fun `generate unions`() { val union = model.lookup("com.test#MyUnion") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - val data = Node.parse( - """{ - "stringVariant": "ok!" - }""", - ) - val writer = RustWriter.forModule("model") - UnionGenerator(model, symbolProvider, writer, union).render() - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(this, union, data) + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) + val data = Node.parse("""{ "stringVariant": "ok!" }""") + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + UnionGenerator(model, symbolProvider, writer, union).render() + writer.unitTest("generate_unions") { + withBlock("let result = ", ";") { + sut.render(this, union, data) + } + rust("assert_eq!(result, MyUnion::StringVariant(\"ok!\".to_owned()));") } - writer.write("assert_eq!(result, MyUnion::StringVariant(\"ok!\".to_string()));") } + project.compileAndTest() } @Test fun `generate struct builders`() { val structure = model.lookup("com.test#MyStruct") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) val data = Node.parse("""{ "bar": 10, "foo": "hello" }""") - val writer = RustWriter.forModule("model") - structure.renderWithModelBuilder(model, symbolProvider, writer) - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(this, structure, data) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + structure.renderWithModelBuilder(model, symbolProvider, writer) + writer.unitTest("generate_struct_builders") { + withBlock("let result = ", ";") { + sut.render(this, structure, data) + } + rust( + """ + assert_eq!(result.bar, 10); + assert_eq!(result.foo.unwrap(), "hello"); + """, + ) } - writer.write("assert_eq!(result.bar, 10);") - writer.write("assert_eq!(result.foo.unwrap(), \"hello\");") } - writer.compileAndTest() + project.compileAndTest() } @Test fun `generate builders for boxed structs`() { val structure = model.lookup("com.test#WithBox") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) val data = Node.parse( - """ { - "member": { - "member": { } - }, "value": 10 + """ + { + "member": { + "member": { } + }, + "value": 10 } - """.trimIndent(), + """, ) - val writer = RustWriter.forModule("model") - structure.renderWithModelBuilder(model, symbolProvider, writer) - writer.test { - withBlock("let result = ", ";") { - sut.render(this, structure, data) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + structure.renderWithModelBuilder(model, symbolProvider, writer) + writer.unitTest("generate_builders_for_boxed_structs") { + withBlock("let result = ", ";") { + sut.render(this, structure, data) + } + rust( + """ + assert_eq!(result, WithBox { + value: Some(10), + member: Some(Box::new(WithBox { + value: None, + member: Some(Box::new(WithBox { value: None, member: None })), + })) + }); + """, + ) } - rust( - """ - assert_eq!(result, WithBox { - value: Some(10), - member: Some(Box::new(WithBox { - value: None, - member: Some(Box::new(WithBox { value: None, member: None })), - })) - }); - """, - ) } - writer.compileAndTest() + project.compileAndTest() } @Test fun `generate lists`() { - val data = Node.parse( - """ [ - "bar", - "foo" - ] - """, - ) - val writer = RustWriter.forModule("lib") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(writer, model.lookup("com.test#MyList"), data) + val data = Node.parse("""["bar", "foo"]""") + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + writer.unitTest("generate_lists") { + writer.withBlock("let result = ", ";") { + sut.render(writer, model.lookup("com.test#MyList"), data) + } + writer.rust("""assert_eq!(result, vec!["bar".to_owned(), "foo".to_owned()]);""") } - writer.write("""assert_eq!(result, vec!["bar".to_string(), "foo".to_string()]);""") } - writer.compileAndTest() + project.compileAndTest() } @Test fun `generate sparse lists`() { - val data = Node.parse( - """ [ - "bar", - "foo", - null - ] - """, - ) - val writer = RustWriter.forModule("lib") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(writer, model.lookup("com.test#MySparseList"), data) + val data = Node.parse(""" [ "bar", "foo", null ] """) + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + writer.unitTest("generate_sparse_lists") { + writer.withBlock("let result = ", ";") { + sut.render(writer, model.lookup("com.test#MySparseList"), data) + } + writer.rust("""assert_eq!(result, vec![Some("bar".to_owned()), Some("foo".to_owned()), None]);""") } - writer.write("""assert_eq!(result, vec![Some("bar".to_string()), Some("foo".to_string()), None]);""") } - writer.compileAndTest() + project.compileAndTest() } @Test fun `generate maps of maps`() { val data = Node.parse( - """{ - "k1": { "map": {} }, - "k2": { "map": { "k3": {} } }, - "k3": { } + """ + { + "k1": { "map": {} }, + "k2": { "map": { "k3": {} } }, + "k3": { } } """, ) - val writer = RustWriter.forModule("model") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - val inner: StructureShape = model.lookup("com.test#Inner") - inner.renderWithModelBuilder(model, symbolProvider, writer) - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(writer, model.lookup("com.test#NestedMap"), data) + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) + val inner = model.lookup("com.test#Inner") + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + inner.renderWithModelBuilder(model, symbolProvider, writer) + writer.unitTest("generate_maps_of_maps") { + writer.withBlock("let result = ", ";") { + sut.render(writer, model.lookup("com.test#NestedMap"), data) + } + writer.rust( + """ + assert_eq!(result.len(), 3); + assert_eq!(result.get("k1").unwrap().map.as_ref().unwrap().len(), 0); + assert_eq!(result.get("k2").unwrap().map.as_ref().unwrap().len(), 1); + assert_eq!(result.get("k3").unwrap().map, None); + """, + ) } - writer.write( - """ - assert_eq!(result.len(), 3); - assert_eq!(result.get("k1").unwrap().map.as_ref().unwrap().len(), 0); - assert_eq!(result.get("k2").unwrap().map.as_ref().unwrap().len(), 1); - assert_eq!(result.get("k3").unwrap().map, None); - """, - ) } - writer.compileAndTest(clippy = true) + project.compileAndTest(runClippy = true) } @Test fun `blob inputs are binary data`() { // "Parameter values that contain binary data MUST be defined using values // that can be represented in plain text (for example, use "foo" and not "Zm9vCg==")." - val writer = RustWriter.forModule("lib") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.CLIENT) - writer.test { - withBlock("let blob = ", ";") { - sut.render( - this, - BlobShape.builder().id(ShapeId.from("com.example#Blob")).build(), - StringNode.parse("foo".dq()), - ) - } - write("assert_eq!(std::str::from_utf8(blob.as_ref()).unwrap(), \"foo\");") - } - writer.compileAndTest() - } + val sut = Instantiator(symbolProvider, model, runtimeConfig, ::enumFromStringFn) - @Test - fun `generate struct with missing required members`() { - val structure = model.lookup("com.test#MyStructRequired") - val inner = model.lookup("com.test#Inner") - val nestedStruct = model.lookup("com.test#NestedStruct") - val union = model.lookup("com.test#NestedUnion") - val sut = Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.SERVER) - val data = Node.parse("{}") - val writer = RustWriter.forModule("model") - structure.renderWithModelBuilder(model, symbolProvider, writer) - inner.renderWithModelBuilder(model, symbolProvider, writer) - nestedStruct.renderWithModelBuilder(model, symbolProvider, writer) - UnionGenerator(model, symbolProvider, writer, union).render() - writer.test { - writer.withBlock("let result = ", ";") { - sut.render(this, structure, data, Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + writer.unitTest("blob_inputs_are_binary_data") { + withBlock("let blob = ", ";") { + sut.render( + writer, + BlobShape.builder().id(ShapeId.from("com.example#Blob")).build(), + StringNode.parse("foo".dq()), + ) + } + rust("assert_eq!(std::str::from_utf8(blob.as_ref()).unwrap(), \"foo\");") } - writer.write( - """ - use std::collections::HashMap; - use aws_smithy_types::{DateTime, Document}; - - let expected = MyStructRequired { - str: Some("".into()), - primitive_int: 0, - int: Some(0), - ts: Some(DateTime::from_secs(0)), - byte: Some(0), - union: Some(NestedUnion::Struct(NestedStruct { - str: Some("".into()), - num: Some(0), - })), - structure: Some(NestedStruct { - str: Some("".into()), - num: Some(0), - }), - list: Some(vec![]), - map: Some(HashMap::new()), - doc: Some(Document::Object(HashMap::new())), - }; - assert_eq!(result, expected); - """, - ) } - writer.compileAndTest() + project.compileAndTest() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt new file mode 100644 index 0000000000..da788d3538 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator + +private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writable { + rust( + """#T::try_from($data).expect("This is used in tests ONLY")""", + enumSymbol, + ) +} + +class ServerInstantiator(val codegenContext: CodegenContext) : + Instantiator( + codegenContext.symbolProvider, + codegenContext.model, + codegenContext.runtimeConfig, + ::enumFromStringFn, + defaultsForRequiredFields = true, + ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index f4b3bab27a..0f315456b1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -37,9 +37,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors import software.amazon.smithy.rust.codegen.core.testutil.TokioTest @@ -55,6 +53,7 @@ import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerInstantiator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolGenerator import java.util.logging.Logger import kotlin.reflect.KFunction1 @@ -96,9 +95,7 @@ class ServerProtocolTestGenerator( inputT to outputT } - private val instantiator = with(codegenContext) { - Instantiator(symbolProvider, model, runtimeConfig, CodegenTarget.SERVER) - } + private val instantiator = ServerInstantiator(codegenContext) private val codegenScope = arrayOf( "Bytes" to RuntimeType.Bytes, @@ -522,7 +519,7 @@ class ServerProtocolTestGenerator( // Construct a dummy response. withBlock("let response = ", ";") { - instantiator.render(this, outputShape, Node.objectNode(), Instantiator.defaultContext().copy(defaultsForRequiredFields = true)) + instantiator.render(this, outputShape, Node.objectNode()) } if (operationShape.errors.isEmpty()) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 952a0dc73a..70013be1fe 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -750,7 +750,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) } } - val err = if (StructureGenerator.fallibleBuilder(inputShape, symbolProvider)) { + val err = if (StructureGenerator.hasFallibleBuilder(inputShape, symbolProvider)) { "?" } else "" rustTemplate("input.build()$err", *codegenScope) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 4159e732c7..0ac168801c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.model.knowledge.NullableIndex import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -70,7 +69,7 @@ fun serverTestCodegenContext( protocolShapeId: ShapeId? = null, ): ServerCodegenContext = ServerCodegenContext( model, - testSymbolProvider(model), + serverTestSymbolProvider(model), serviceShape ?: model.serviceShapes.firstOrNull() ?: ServiceShape.builder().version("test").id("test#Service").build(), diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt new file mode 100644 index 0000000000..e818115f08 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt @@ -0,0 +1,221 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext + +class ServerInstantiatorTest { + // This model started off from the one in `InstantiatorTest.kt` from `codegen-core`. + private val model = """ + namespace com.test + + use smithy.framework#ValidationException + + @documentation("this documents the shape") + structure MyStruct { + foo: String, + @documentation("This *is* documentation about the member.") + bar: PrimitiveInteger, + baz: Integer, + ts: Timestamp, + byteValue: Byte + } + + list MyList { + member: String + } + + @sparse + list MySparseList { + member: String + } + + union MyUnion { + stringVariant: String, + numVariant: Integer + } + + structure Inner { + map: NestedMap + } + + map NestedMap { + key: String, + value: Inner + } + + structure WithBox { + member: WithBox, + value: Integer + } + + union NestedUnion { + struct: NestedStruct, + int: Integer + } + + structure NestedStruct { + @required + str: String, + @required + num: Integer + } + + structure MyStructRequired { + @required + str: String, + @required + primitiveInt: PrimitiveInteger, + @required + int: Integer, + @required + ts: Timestamp, + @required + byte: Byte + @required + union: NestedUnion, + @required + structure: NestedStruct, + @required + list: MyList, + @required + map: NestedMap, + @required + doc: Document + } + + @enum([ + { value: "t2.nano" }, + { value: "t2.micro" }, + ]) + string UnnamedEnum + + @enum([ + { + value: "t2.nano", + name: "T2_NANO", + }, + { + value: "t2.micro", + name: "T2_MICRO", + }, + ]) + string NamedEnum + """.asSmithyModel().let { RecursiveShapeBoxer.transform(it) } + + private val codegenContext = serverTestCodegenContext(model) + private val symbolProvider = codegenContext.symbolProvider + + @Test + fun `generate struct with missing required members`() { + val structure = model.lookup("com.test#MyStructRequired") + val inner = model.lookup("com.test#Inner") + val nestedStruct = model.lookup("com.test#NestedStruct") + val union = model.lookup("com.test#NestedUnion") + val sut = ServerInstantiator(codegenContext) + val data = Node.parse("{}") + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + structure.renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER) + inner.renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER) + nestedStruct.renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER) + UnionGenerator(model, symbolProvider, writer, union).render() + + writer.unitTest("server_instantiator_test") { + withBlock("let result = ", ";") { + sut.render(this, structure, data) + } + + rust( + """ + use std::collections::HashMap; + use aws_smithy_types::{DateTime, Document}; + + let expected = MyStructRequired { + str: "".to_owned(), + primitive_int: 0, + int: 0, + ts: DateTime::from_secs(0), + byte: 0, + union: NestedUnion::Struct(NestedStruct { + str: "".to_owned(), + num: 0, + }), + structure: NestedStruct { + str: "".to_owned(), + num: 0, + }, + list: Vec::new(), + map: HashMap::new(), + doc: Document::Object(HashMap::new()), + }; + assert_eq!(result, expected); + """, + ) + } + } + project.compileAndTest() + } + + @Test + fun `generate named enums`() { + val shape = model.lookup("com.test#NamedEnum") + val sut = ServerInstantiator(codegenContext) + val data = Node.parse("t2.nano".dq()) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + EnumGenerator(model, symbolProvider, writer, shape, shape.expectTrait()).render() + writer.unitTest("generate_named_enums") { + withBlock("let result = ", ";") { + sut.render(this, shape, data) + } + rust("assert_eq!(result, NamedEnum::T2Nano);") + } + } + project.compileAndTest() + } + + @Test + fun `generate unnamed enums`() { + val shape = model.lookup("com.test#UnnamedEnum") + val sut = ServerInstantiator(codegenContext) + val data = Node.parse("t2.nano".dq()) + + val project = TestWorkspace.testProject() + project.withModule(RustModule.Model) { writer -> + EnumGenerator(model, symbolProvider, writer, shape, shape.expectTrait()).render() + writer.unitTest("generate_unnamed_enums") { + withBlock("let result = ", ";") { + sut.render(this, shape, data) + } + rust("""assert_eq!(result, UnnamedEnum("t2.nano".to_owned()));""") + } + } + project.compileAndTest() + } +} From 7439f3d7f662d4e48d2e2010e7d0e95ea5527a7f Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 19 Oct 2022 10:44:05 +0200 Subject: [PATCH 238/255] ./gradlew ktlintFormat --- .../codegen/client/smithy/generators/ClientInstantiator.kt | 4 +--- .../rust/codegen/core/smithy/generators/InstantiatorTest.kt | 2 +- .../smithy/protocols/parse/AwsQueryParserGeneratorTest.kt | 1 + .../smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt | 1 + .../core/smithy/protocols/parse/JsonParserGeneratorTest.kt | 1 + .../protocols/parse/XmlBindingTraitParserGeneratorTest.kt | 1 + .../codegen/server/smithy/generators/ServerInstantiator.kt | 4 +--- .../server/smithy/generators/ServerInstantiatorTest.kt | 2 -- 8 files changed, 7 insertions(+), 9 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt index 615ac37a55..a059dac799 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -15,15 +15,13 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName -import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writable { rust("#T::from($data)", enumSymbol) } // TODO Move to a separate file. -class ClientBuilderKindBehavior(val codegenContext: CodegenContext): Instantiator.BuilderKindBehavior { +class ClientBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { override fun hasFallibleBuilder(shape: StructureShape) = StructureGenerator.hasFallibleBuilder(shape, codegenContext.symbolProvider) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt index a968ce1a66..67d15c2051 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt @@ -89,7 +89,7 @@ class InstantiatorTest { private val runtimeConfig = codegenContext.runtimeConfig // This is the exact same behavior of the client. - private class BuilderKindBehavior(val codegenContext: CodegenContext): Instantiator.BuilderKindBehavior { + private class BuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { override fun hasFallibleBuilder(shape: StructureShape) = StructureGenerator.hasFallibleBuilder(shape, codegenContext.symbolProvider) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt index 5a109a4128..f6ee70cb36 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt @@ -46,6 +46,7 @@ class AwsQueryParserGeneratorTest { fun `it modifies operation parsing to include Response and Result tags`() { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) val symbolProvider = testSymbolProvider(model) + // TODO We should grep for all of these and move them somewhere central. fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt index be72aa92ee..097a51eb5c 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt @@ -47,6 +47,7 @@ class Ec2QueryParserGeneratorTest { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) + // TODO We should grep for all of these and move them somewhere central. fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt index 211323ece0..2db18c2656 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt @@ -118,6 +118,7 @@ class JsonParserGeneratorTest { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) + // TODO We should grep for all of these and move them somewhere central. fun returnSymbolToParse(shape: Shape): Pair = false to symbolProvider.toSymbol(shape) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt index 9dbf0ad20b..88593da3c2 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt @@ -95,6 +95,7 @@ internal class XmlBindingTraitParserGeneratorTest { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) + // TODO We should grep for all of these and move them somewhere central. fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt index ca03f66ada..bce21f93bd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -6,8 +6,6 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators 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.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -27,7 +25,7 @@ private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writa } // TODO Move to a separate file. -class ServerBuilderKindBehavior(val codegenContext: CodegenContext): Instantiator.BuilderKindBehavior { +class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { override fun hasFallibleBuilder(shape: StructureShape): Boolean { // Only operation input builders take in unconstrained types. val takesInUnconstrainedTypes = shape.isReachableFromOperationInput() diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt index 9881a89496..cb0715f6c8 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiatorTest.kt @@ -13,14 +13,12 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.withBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait From 824d6d79f705811b6221677698f411b26cb380c9 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 21 Oct 2022 21:45:26 +0200 Subject: [PATCH 239/255] Delete ConstraintsButThisShouldNotStayInCore --- .../ConstraintsButThisShouldNotStayInCore.kt | 56 ------------------ .../smithy/generators/StructureGenerator.kt | 26 ++------- .../generators/http/HttpBindingGenerator.kt | 57 ++++++++++++++----- .../protocols/parse/JsonParserGenerator.kt | 33 ++++++++--- .../serialize/JsonSerializerGenerator.kt | 19 ++++--- .../smithy/ConstrainedShapeSymbolProvider.kt | 1 - .../ConstraintViolationSymbolProvider.kt | 1 - .../rust/codegen/server/smithy/Constraints.kt | 57 ++++++++++++++++--- .../server/smithy/ServerCodegenVisitor.kt | 2 - .../UnconstrainedShapeSymbolProvider.kt | 2 - ...BeforeIteratingOverMapJsonCustomization.kt | 12 ++-- .../ConstrainedMapGeneratorCommon.kt | 4 +- .../PubCrateConstrainedCollectionGenerator.kt | 4 +- .../PubCrateConstrainedMapGenerator.kt | 4 +- .../ServerBuilderConstraintViolations.kt | 2 +- .../generators/ServerBuilderGenerator.kt | 32 +++++++++-- .../smithy/generators/ServerInstantiator.kt | 3 +- .../UnconstrainedCollectionGenerator.kt | 2 +- .../generators/UnconstrainedMapGenerator.kt | 5 +- .../generators/UnconstrainedUnionGenerator.kt | 6 +- .../http/ServerRequestBindingGenerator.kt | 41 ++++++++++++- .../http/ServerResponseBindingGenerator.kt | 4 +- .../generators/protocol/ServerProtocol.kt | 48 +++++++++++++++- .../server/smithy/protocols/ServerAwsJson.kt | 12 ++-- .../ServerHttpBoundProtocolGenerator.kt | 12 +++- .../codegen/server/smithy/ConstraintsTest.kt | 2 - 26 files changed, 283 insertions(+), 164 deletions(-) delete mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt deleted file mode 100644 index f064d424de..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/ConstraintsButThisShouldNotStayInCore.kt +++ /dev/null @@ -1,56 +0,0 @@ -package software.amazon.smithy.rust.codegen.core.smithy - -import software.amazon.smithy.codegen.core.SymbolProvider -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.neighbor.Walker -import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.MemberShape -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.model.traits.EnumTrait -import software.amazon.smithy.model.traits.LengthTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait - -// TODO Move to `Constraints.kt` in `codegen-server`. I can't yet because we need to split `Instantiator.kt`. - -fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - if (this is MemberShape) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented - // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, - // so we can't simply delegate to the `else` branch when we implement them. - this.targetCanReachConstrainedShape(model, symbolProvider) - } else { - Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } - } - -fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = - model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) - -/** - * We say a shape is _directly_ constrained if: - * - * - it has a constraint trait, or; - * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. - * - * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is - * a constrained shape is _not_ directly constrained. - * - * At the moment only a subset of constraint traits are implemented on a subset of shapes; that's why we match against - * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's - * (with these subsets being smaller than what [the spec] accounts for). - * - * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html - */ -fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when (this) { - is StructureShape -> { - // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): - // The only reason why the functions in this file have - // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as - // `required`, so we can't use `member.isOptional` here. - this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } - } - is MapShape -> this.hasTrait() - is StringShape -> this.hasTrait() || this.hasTrait() - else -> false -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index f24472d8e8..5da053fc16 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -28,7 +28,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator @@ -82,6 +81,8 @@ open class StructureGenerator( /** * Returns whether a structure shape, whose builder has been generated with [BuilderGenerator], requires a * fallible builder to be constructed. + * + * TODO Move this to `BuilderGenerator`. */ fun hasFallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = // All operation inputs should have fallible builders in case a new required field is added in the future. @@ -94,32 +95,13 @@ open class StructureGenerator( !it.isOptional() && !it.canUseDefault() } - /** - * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a - * fallible builder to be constructed. - * - * TODO Move this to `codegen-server`. I can't yet because we need to split `Instantiator.kt`. - */ - fun serverHasFallibleBuilder( - structureShape: StructureShape, - model: Model, - symbolProvider: SymbolProvider, - takeInUnconstrainedTypes: Boolean, - ): Boolean = - if (takeInUnconstrainedTypes) { - structureShape.canReachConstrainedShape(model, symbolProvider) - } else { - structureShape - .members() - .map { symbolProvider.toSymbol(it) } - .any { !it.isOptional() } - } - /** * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGeneratorWithoutPublicConstrainedTypes], * requires a fallible builder to be constructed. * * This builder only enforces the `required` trait. + * + * TODO Move this to `codegen-server`, to ServerBuilderGeneratorWithoutPublicConstrainedTypes */ fun serverHasFallibleBuilderWithoutPublicConstrainedTypes( structureShape: StructureShape, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index d736203361..fc2c9f7f53 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -13,10 +13,12 @@ import software.amazon.smithy.model.knowledge.HttpBindingIndex import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.DocumentShape +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.Shape +import software.amazon.smithy.model.shapes.SimpleShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape @@ -40,7 +42,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError @@ -51,7 +52,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.EventStreamUnmarshallerGenerator import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -82,6 +82,8 @@ sealed class HttpBindingSection(name: String) : Section(name) { // Addressed in https://github.com/awslabs/smithy-rs/pull/1841 data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: Shape) : HttpBindingSection("BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders") + data class AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(val memberShape: MemberShape) : + HttpBindingSection("AfterDeserializingIntoAHashMapOfHttpPrefixHeaders") } typealias HttpBindingCustomization = NamedSectionGenerator @@ -154,11 +156,13 @@ class HttpBindingGenerator( fun generateDeserializePrefixHeaderFn(binding: HttpBindingDescriptor): RuntimeType { check(binding.location == HttpBinding.Location.PREFIX_HEADERS) - val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && binding.member.targetCanReachConstrainedShape(model, symbolProvider) + // TODO Remove +// val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && binding.member.targetCanReachConstrainedShape(model, symbolProvider) val outputSymbol = symbolProvider.toSymbol(binding.member) - if (!returnUnconstrainedType) { - check(outputSymbol.rustType().stripOuter() is RustType.HashMap) { outputSymbol.rustType() } - } + // TODO Remove +// if (!returnUnconstrainedType) { +// check(outputSymbol.rustType().stripOuter() is RustType.HashMap) { outputSymbol.rustType() } +// } val target = model.expectShape(binding.member.target) check(target is MapShape) val fnName = "deser_prefix_header_${fnName(operationShape, binding)}" @@ -193,11 +197,17 @@ class HttpBindingGenerator( headerUtil, inner, ) - if (returnUnconstrainedType) { - // If the map shape has constrained string keys or values, we need to wrap the deserialized hash map - // in the corresponding unconstrained wrapper tuple struct. - rust("let out = out.map(#T);", outputSymbol.mapRustType { it.stripOuter() }) + for (customization in customizations) { + customization.section( + HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(binding.member), + )(this) } + // TODO Remove +// if (returnUnconstrainedType) { +// // If the map shape has constrained string keys or values, we need to wrap the deserialized hash map +// // in the corresponding unconstrained wrapper tuple struct. +// rust("let out = out.map(#T);", outputSymbol.mapRustType { it.stripOuter() }) +// } rust("out.map(Some)") } } @@ -406,6 +416,7 @@ class HttpBindingGenerator( }) """, ) + // TODO Is this arm reachable? Nothing should render to a `HashSet`. is RustType.HashSet -> rust( """ @@ -417,10 +428,10 @@ class HttpBindingGenerator( """, ) else -> { - val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && - targetShape is CollectionShape && - targetShape.canReachConstrainedShape(model, symbolProvider) - if (returnUnconstrainedType) { + if (targetShape is ListShape) { + // This is a constrained list shape and we must therefore be generating a server SDK. + check(codegenTarget == CodegenTarget.SERVER) + check(rustType is RustType.Opaque) rust( """ Ok(if !$parsedValue.is_empty() { @@ -432,6 +443,23 @@ class HttpBindingGenerator( symbolProvider.toSymbol(targetShape), ) } else { + check(targetShape is SimpleShape) + // TODO Remove +// val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && +// targetShape is CollectionShape && +// targetShape.canReachConstrainedShape(model, symbolProvider) +// if (returnUnconstrainedType) { +// rust( +// """ +// Ok(if !$parsedValue.is_empty() { +// Some(#T($parsedValue)) +// } else { +// None +// }) +// """, +// symbolProvider.toSymbol(targetShape), +// ) +// } else { rustTemplate( """ if $parsedValue.len() > 1 { @@ -443,6 +471,7 @@ class HttpBindingGenerator( """, "header_util" to headerUtil, ) +// } } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 992393f6c1..020c0d72b5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -39,6 +39,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant @@ -48,7 +50,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.deserializeFunctionName -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -56,6 +57,19 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.utils.StringUtils +/** + * Class describing a JSON parser section that can be used in a customization. + */ +sealed class JsonParserSection(name: String) : Section(name) { + // TODO Docs + data class BeforeBoxingDeserializedMember(val shape: MemberShape) : JsonParserSection("BeforeBoxingDeserializedMember") +} + +/** + * Customization for the JSON parser. + */ +typealias JsonParserCustomization = NamedSectionGenerator + class JsonParserGenerator( codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, @@ -76,6 +90,7 @@ class JsonParserGenerator( * this function should just need to return a `Symbol` as opposed to a pair. */ private val returnSymbolToParse: (Shape) -> Pair, + private val customizations: List = listOf(), ) : StructuredDataParserGenerator { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider @@ -272,13 +287,17 @@ class JsonParserGenerator( } val symbol = symbolProvider.toSymbol(memberShape) if (symbol.isRustBoxed()) { - if (codegenTarget == CodegenTarget.SERVER && - model.expectShape(memberShape.container).isStructureShape && - memberShape.targetCanReachConstrainedShape(model, symbolProvider) - ) { - // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. - rust(".map(|x| x.into())") + for (customization in customizations) { + customization.section(JsonParserSection.BeforeBoxingDeserializedMember(memberShape))(this) } + // TODO Remove +// if (codegenTarget == CodegenTarget.SERVER && +// model.expectShape(memberShape.container).isStructureShape && +// memberShape.targetCanReachConstrainedShape(model, symbolProvider) +// ) { +// // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. +// rust(".map(|x| x.into())") +// } rust(".map(Box::new)") } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index 194806d963..b39a7401a4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -51,26 +51,27 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape /** - * Class describing a JSON section that can be used in a customization. + * Class describing a JSON serializer section that can be used in a customization. */ -sealed class JsonSection(name: String) : Section(name) { +sealed class JsonSerializerSection(name: String) : Section(name) { /** Mutate the server error object prior to finalization. Eg: this can be used to inject `__type` to record the error type. */ - data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSection("ServerError") + data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSerializerSection("ServerError") - data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSection("BeforeIteratingOverMap") + // TODO Docs + data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSerializerSection("BeforeIteratingOverMap") } /** - * JSON customization. + * Customization for the JSON serializer. */ -typealias JsonCustomization = NamedSectionGenerator +typealias JsonSerializerCustomization = NamedSectionGenerator class JsonSerializerGenerator( codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, /** Function that maps a MemberShape into a JSON field name */ private val jsonName: (MemberShape) -> String, - private val customizations: List = listOf(), + private val customizations: List = listOf(), ) : StructuredDataSerializerGenerator { private data class Context( /** Expression that retrieves a JsonValueWriter from either a JsonObjectWriter or JsonArrayWriter */ @@ -184,7 +185,7 @@ class JsonSerializerGenerator( rust("let mut out = String::new();") rustTemplate("let mut object = #{JsonObjectWriter}::new(&mut out);", *codegenScope) serializeStructure(StructContext("object", "value", structureShape), includedMembers) - customizations.forEach { customization -> customization.section(JsonSection.ServerError(structureShape, "object"))(this) } + customizations.forEach { customization -> customization.section(JsonSerializerSection.ServerError(structureShape, "object"))(this) } rust("object.finish();") rustTemplate("Ok(out)", *codegenScope) } @@ -424,7 +425,7 @@ class JsonSerializerGenerator( val keyName = safeName("key") val valueName = safeName("value") for (customization in customizations) { - customization.section(JsonSection.BeforeIteratingOverMap(context.shape, context.valueExpression))(this) + customization.section(JsonSerializerSection.BeforeIteratingOverMap(context.shape, context.valueExpression))(this) } rustBlock("for ($keyName, $valueName) in ${context.valueExpression.asRef()}") { val keyExpression = "$keyName.as_str()" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt index 7e4f9a3960..92ff5faf77 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolProvider.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt index 4af4d55514..119db96452 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintViolationSymbolProvider.kt @@ -19,7 +19,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.Models import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.toSnakeCase diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt index 2fb5671879..82102be18a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/Constraints.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model +import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape @@ -21,8 +22,7 @@ import software.amazon.smithy.model.traits.PatternTrait import software.amazon.smithy.model.traits.RangeTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.UniqueItemsTrait -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -42,6 +42,53 @@ fun Shape.hasConstraintTrait() = hasTrait() || hasTrait() +/** + * We say a shape is _directly_ constrained if: + * + * - it has a constraint trait, or; + * - in the case of it being an aggregate shape, one of its member shapes has a constraint trait. + * + * Note that an aggregate shape whose member shapes do not have constraint traits but that has a member whose target is + * a constrained shape is _not_ directly constrained. + * + * At the moment only a subset of constraint traits are implemented on a subset of shapes; that's why we match against + * a subset of shapes in each arm, and check for a subset of constraint traits attached to the shape in the arm's + * (with these subsets being smaller than what [the spec] accounts for). + * + * [the spec]: https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html + */ +fun Shape.isDirectlyConstrained(symbolProvider: SymbolProvider): Boolean = when (this) { + is StructureShape -> { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302, https://github.com/awslabs/smithy/issues/1179): + // The only reason why the functions in this file have + // to take in a `SymbolProvider` is because non-`required` blob streaming members are interpreted as + // `required`, so we can't use `member.isOptional` here. + this.members().map { symbolProvider.toSymbol(it) }.any { !it.isOptional() } + } + is MapShape -> this.hasTrait() + is StringShape -> this.hasTrait() || this.hasTrait() + else -> false +} + +fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider): Boolean = + this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) + +fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider): Boolean = + !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) + +fun Shape.canReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + if (this is MemberShape) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1401) Constraint traits on member shapes are not implemented + // yet. Also, note that a walker over a member shape can, perhaps counterintuitively, reach the _containing_ shape, + // so we can't simply delegate to the `else` branch when we implement them. + this.targetCanReachConstrainedShape(model, symbolProvider) + } else { + Walker(model).walkShapes(this).toSet().any { it.isDirectlyConstrained(symbolProvider) } + } + +fun MemberShape.targetCanReachConstrainedShape(model: Model, symbolProvider: SymbolProvider): Boolean = + model.expectShape(this.target).canReachConstrainedShape(model, symbolProvider) + fun Shape.hasPublicConstrainedWrapperTupleType(model: Model, publicConstrainedTypes: Boolean): Boolean = when (this) { is MapShape -> publicConstrainedTypes && this.hasTrait() is StringShape -> !this.hasTrait() && (publicConstrainedTypes && this.hasTrait()) @@ -84,9 +131,3 @@ fun Shape.typeNameContainsNonPublicType( is StructureShape, is UnionShape -> false else -> UNREACHABLE("the above arms should be exhaustive, but we received shape: $this") } - -fun MemberShape.hasConstraintTraitOrTargetHasConstraintTrait(model: Model, symbolProvider: SymbolProvider): Boolean = - this.isDirectlyConstrained(symbolProvider) || (model.expectShape(this.target).isDirectlyConstrained(symbolProvider)) - -fun Shape.isTransitivelyButNotDirectlyConstrained(model: Model, symbolProvider: SymbolProvider): Boolean = - !this.isDirectlyConstrained(symbolProvider) && this.canReachConstrainedShape(model, symbolProvider) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 4bb8244ac4..9ec7248b30 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -33,11 +33,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt index 4b1673ac0b..9fa2182e6b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProvider.kt @@ -22,13 +22,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.Unconstrained import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.handleOptionality import software.amazon.smithy.rust.codegen.core.smithy.handleRustBoxing import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.setDefault import software.amazon.smithy.rust.codegen.core.smithy.symbolBuilder -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt index 5361ba3929..96b3295d28 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt @@ -3,8 +3,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.customizations import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerCustomization +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerSection import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType @@ -12,10 +12,10 @@ import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstr * A customization to, just before we iterate over a _constrained_ map shape in a JSON serializer, unwrap the wrapper * newtype and take a shared reference to the actual `std::collections::HashMap` within it. */ -class BeforeIteratingOverMapJsonCustomization(private val codegenContext: ServerCodegenContext) : JsonCustomization() { - override fun section(section: JsonSection): Writable = when (section) { - is JsonSection.ServerError -> emptySection - is JsonSection.BeforeIteratingOverMap -> writable { +class BeforeIteratingOverMapJsonCustomization(private val codegenContext: ServerCodegenContext) : JsonSerializerCustomization() { + override fun section(section: JsonSerializerSection): Writable = when (section) { + is JsonSerializerSection.ServerError -> emptySection + is JsonSerializerSection.BeforeIteratingOverMap -> writable { if (workingWithPublicConstrainedWrapperTupleType( section.shape, codegenContext.model, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt index 5a2b9d6c68..fb5ce1daee 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorCommon.kt @@ -9,8 +9,8 @@ import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StringShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained /** * Common helper functions used in [UnconstrainedMapGenerator] and [MapConstraintViolationGenerator]. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt index 7c32b02e12..b789c2166d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedCollectionGenerator.kt @@ -13,9 +13,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt index 8d82d37d60..591b11b7ed 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/PubCrateConstrainedMapGenerator.kt @@ -14,9 +14,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.isTransitivelyButNotDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.typeNameContainsNonPublicType diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 11be4549a1..3448690a44 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -15,13 +15,13 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait 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.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape /** * Renders constraint violation types that arise when building a structure shape builder. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index 8a15370858..a0be003880 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -6,6 +6,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators 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.StructureShape import software.amazon.smithy.model.shapes.UnionShape @@ -30,7 +32,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.isRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained @@ -38,7 +39,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -46,7 +46,9 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTraitOrTargetHasConstraintTrait +import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled /** @@ -86,6 +88,29 @@ class ServerBuilderGenerator( codegenContext: ServerCodegenContext, private val shape: StructureShape, ) { + companion object { + /** + * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a + * fallible builder to be constructed. + * + * TODO Rename to `hasFallibleBuilder`, make sure usages are using it as `ServerBuilderGenerator.hasFallibleBuilder`. + */ + fun serverHasFallibleBuilder( + structureShape: StructureShape, + model: Model, + symbolProvider: SymbolProvider, + takeInUnconstrainedTypes: Boolean, + ): Boolean = + if (takeInUnconstrainedTypes) { + structureShape.canReachConstrainedShape(model, symbolProvider) + } else { + structureShape + .members() + .map { symbolProvider.toSymbol(it) } + .any { !it.isOptional() } + } + } + private val takeInUnconstrainedTypes = shape.isReachableFromOperationInput() private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig @@ -98,8 +123,7 @@ class ServerBuilderGenerator( private val structureSymbol = symbolProvider.toSymbol(shape) private val builderSymbol = shape.serverBuilderSymbol(codegenContext) private val moduleName = builderSymbol.namespace.split(builderSymbol.namespaceDelimiter).last() - private val isBuilderFallible = - StructureGenerator.serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val isBuilderFallible = serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) private val serverBuilderConstraintViolations = ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt index 5e65a0e521..f1782ea360 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput @@ -34,7 +33,7 @@ class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat override fun hasFallibleBuilder(shape: StructureShape): Boolean { // Only operation input builders take in unconstrained types. val takesInUnconstrainedTypes = shape.isReachableFromOperationInput() - return StructureGenerator.serverHasFallibleBuilder( + return ServerBuilderGenerator.serverHasFallibleBuilder( shape, codegenContext.model, codegenContext.symbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 932c4702eb..52e9f7a9ef 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -12,11 +12,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt index 0b87cbbe9a..4d47eb6229 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGenerator.kt @@ -12,16 +12,15 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.join -import software.amazon.smithy.rust.codegen.core.rustlang.plus import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained /** * Generates a Rust type for a constrained map shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index cd48a39a6d..842a3b4413 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -22,11 +22,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed -import software.amazon.smithy.rust.codegen.core.smithy.targetCanReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -34,6 +31,9 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained +import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape /** * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 22ee9a0ed4..f866d83e3a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -8,14 +8,23 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.http import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType +import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol +import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape class ServerRequestBindingGenerator( protocol: Protocol, @@ -27,7 +36,18 @@ class ServerRequestBindingGenerator( !codegenContext.settings.codegenConfig.publicConstrainedTypes, ) private val httpBindingGenerator = - HttpBindingGenerator(protocol, codegenContext, codegenContext.unconstrainedShapeSymbolProvider, operationShape, ::serverBuilderSymbol) + HttpBindingGenerator( + protocol, + codegenContext, + codegenContext.unconstrainedShapeSymbolProvider, + operationShape, + ::serverBuilderSymbol, + listOf( + ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUnconstrainedMapHttpBindingCustomization( + codegenContext, + ), + ), + ) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = httpBindingGenerator.generateDeserializeHeaderFn(binding) @@ -47,3 +67,22 @@ class ServerRequestBindingGenerator( binding: HttpBindingDescriptor, ): RuntimeType = httpBindingGenerator.generateDeserializePrefixHeaderFn(binding) } + +/** + * A customization to, just after we've deserialized HTTP request headers bound to a map shape via `@httpPrefixHeaders`, + * wrap the `std::collections::HashMap` in an unconstrained type wrapper newtype. + */ +class ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUnconstrainedMapHttpBindingCustomization(val codegenContext: ServerCodegenContext) : + HttpBindingCustomization() { + override fun section(section: HttpBindingSection): Writable = when (section) { + is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders -> emptySection + is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders -> writable { + if (section.memberShape.targetCanReachConstrainedShape(codegenContext.model, codegenContext.unconstrainedShapeSymbolProvider)) { + rust( + "let out = out.map(#T);", + codegenContext.unconstrainedShapeSymbolProvider.toSymbol(section.memberShape).mapRustType { it.stripOuter() }, + ) + } + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index ff675104d3..7c402ca3b2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -27,6 +27,7 @@ class ServerResponseBindingGenerator( private val codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { + // TODO Why is this not using serverBuilderSymbol like `ServerRequestBindingGenerator`? private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) private val httpBindingGenerator = @@ -48,7 +49,7 @@ class ServerResponseBindingGenerator( } /** - * A customization to, just before we iterate over a _constrained_ map shape that is bound to HTTP headers via + * A customization to, just before we iterate over a _constrained_ map shape that is bound to HTTP response headers via * `@httpPrefixHeaders`, unwrap the wrapper newtype and take a shared reference to the actual `std::collections::HashMap` * within it. */ @@ -65,5 +66,6 @@ class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstr rust("let ${section.variableName} = &${section.variableName}.0;") } } + is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index e66e69c359..2ee45817c6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion @@ -26,16 +25,20 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonSerializerGenerator +import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape private fun allOperations(codegenContext: CodegenContext): List { val index = TopDownIndex.of(codegenContext.model) @@ -109,7 +112,16 @@ class ServerAwsJsonProtocol( } else { false to codegenContext.symbolProvider.toSymbol(shape) } - return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + return JsonParserGenerator( + codegenContext, + httpBindingResolver, + ::awsJsonFieldName, + ::builderSymbol, + ::returnSymbolToParse, + listOf( + ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(serverCodegenContext), + ), + ) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = @@ -240,7 +252,18 @@ class ServerRestJsonProtocol( } else { false to codegenContext.symbolProvider.toSymbol(shape) } - return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + return JsonParserGenerator( + codegenContext, + httpBindingResolver, + ::restJsonFieldName, + ::builderSymbol, + ::returnSymbolToParse, + listOf( + ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization( + serverCodegenContext, + ), + ), + ) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = @@ -296,3 +319,22 @@ class ServerRestXmlProtocol( override fun serverContentTypeCheckNoModeledInput() = true } + +/** + * A customization to, just before we box a recursive member that we've deserialized into `Option`, convert it into + * `MaybeConstrained` if the target shape can reach a constrained shape. + */ +class ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(val codegenContext: ServerCodegenContext) : + JsonParserCustomization() { + override fun section(section: JsonParserSection): Writable = when (section) { + is JsonParserSection.BeforeBoxingDeserializedMember -> writable { + // We're only interested in _structure_ member shapes that can reach constrained shapes. + if ( + codegenContext.model.expectShape(section.shape.container) is StructureShape && + section.shape.targetCanReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider) + ) { + rust(".map(|x| x.into())") + } + } + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 92908cdd90..4ca26f7598 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -15,9 +15,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonCustomization -import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -56,9 +56,9 @@ class ServerAwsJsonFactory(private val version: AwsJsonVersion) : * AwsJson requires errors to be serialized in server responses with an additional `__type` field. This * customization writes the right field depending on the version of the AwsJson protocol. */ -class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonCustomization() { - override fun section(section: JsonSection): Writable = when (section) { - is JsonSection.ServerError -> writable { +class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonSerializerCustomization() { + override fun section(section: JsonSerializerSection): Writable = when (section) { + is JsonSerializerSection.ServerError -> writable { if (section.structureShape.hasTrait()) { val typeId = when (awsJsonVersion) { // AwsJson 1.0 wants the whole shape ID (namespace#Shape). @@ -71,7 +71,7 @@ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonCusto rust("""${section.jsonObject}.key("__type").string("${escape(typeId)}");""") } } - is JsonSection.BeforeIteratingOverMap -> emptySection + is JsonSerializerSection.BeforeIteratingOverMap -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index e1266616d4..e5b5c1fd05 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -43,9 +43,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType @@ -74,6 +72,8 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerRequestBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol @@ -767,7 +767,13 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) } } - val err = if (StructureGenerator.serverHasFallibleBuilder(inputShape, model, symbolProvider, takeInUnconstrainedTypes = true)) { + val err = if (ServerBuilderGenerator.serverHasFallibleBuilder( + inputShape, + model, + symbolProvider, + takeInUnconstrainedTypes = true, + ) + ) { "?" } else "" rustTemplate("input.build()$err", *codegenScope) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt index c1643900b3..80e2d93dae 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt @@ -13,8 +13,6 @@ import software.amazon.smithy.model.shapes.MapShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.canReachConstrainedShape -import software.amazon.smithy.rust.codegen.core.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider From 21a8df393a889fa14267781473516167c5a501d4 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 21 Oct 2022 22:50:07 +0200 Subject: [PATCH 240/255] Address some TODOs --- .../smithy/generators/BuilderGenerator.kt | 4 ++++ .../smithy/generators/StructureGenerator.kt | 17 -------------- .../generators/http/HttpBindingGenerator.kt | 4 +--- .../codegen/core/smithy/protocols/AwsJson.kt | 13 ++++++----- .../parse/EventStreamUnmarshallerGenerator.kt | 1 - .../parse/XmlBindingTraitParserGenerator.kt | 1 - .../parse/AwsQueryParserGeneratorTest.kt | 9 ++------ .../parse/Ec2QueryParserGeneratorTest.kt | 8 ++----- .../XmlBindingTraitParserGeneratorTest.kt | 8 ++----- .../server/smithy/ServerCodegenVisitor.kt | 2 +- .../generators/ConstrainedStringGenerator.kt | 2 +- .../MapConstraintViolationGenerator.kt | 2 +- .../generators/ServerBuilderGenerator.kt | 8 +++---- ...rGeneratorWithoutPublicConstrainedTypes.kt | 22 ++++++++++++++++--- .../smithy/generators/ServerEnumGenerator.kt | 2 +- .../smithy/generators/ServerInstantiator.kt | 4 ++-- .../UnconstrainedCollectionGenerator.kt | 2 +- .../generators/UnconstrainedUnionGenerator.kt | 2 +- .../ServerHttpBoundProtocolGenerator.kt | 2 +- ...hapeReachableFromOperationInputTagTrait.kt | 6 ++--- ...ShapesReachableFromOperationInputTagger.kt | 2 +- 21 files changed, 53 insertions(+), 68 deletions(-) rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt (90%) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 4a42807315..aa334d79fe 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -39,6 +39,10 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase // TODO This builder generator is only used by the client. // Move this entire file, and its tests, to `codegen-client`. It's not as easy as it seems. +fun builderSymbolFn(symbolProvider: RustSymbolProvider): (StructureShape) -> Symbol = { structureShape -> + structureShape.builderSymbol(symbolProvider) +} + fun StructureShape.builderSymbol(symbolProvider: RustSymbolProvider): Symbol { val structureSymbol = symbolProvider.toSymbol(this) val builderNamespace = RustReservedWords.escapeIfNeeded(structureSymbol.name.toSnakeCase()) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 5da053fc16..9c0d7c101e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -94,23 +94,6 @@ open class StructureGenerator( // generate a fallible builder. !it.isOptional() && !it.canUseDefault() } - - /** - * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGeneratorWithoutPublicConstrainedTypes], - * requires a fallible builder to be constructed. - * - * This builder only enforces the `required` trait. - * - * TODO Move this to `codegen-server`, to ServerBuilderGeneratorWithoutPublicConstrainedTypes - */ - fun serverHasFallibleBuilderWithoutPublicConstrainedTypes( - structureShape: StructureShape, - symbolProvider: SymbolProvider, - ): Boolean = - structureShape - .members() - .map { symbolProvider.toSymbol(it) } - .any { !it.isOptional() } } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index fc2c9f7f53..995c47de32 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -78,9 +78,7 @@ enum class HttpMessageType { * Class describing an HTTP binding (de)serialization section that can be used in a customization. */ sealed class HttpBindingSection(name: String) : Section(name) { - // TODO `shape` should be `MapShape`. See the instantiator site, which is working with a `Shape` instead (and should not be). - // Addressed in https://github.com/awslabs/smithy-rs/pull/1841 - data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: Shape) : + data class BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders(val variableName: String, val shape: MapShape) : HttpBindingSection("BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders") data class AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(val memberShape: MemberShape) : HttpBindingSection("AfterDeserializingIntoAHashMapOfHttpPrefixHeaders") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 52b3dff9b4..3056e37e6d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait @@ -22,7 +21,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn import software.amazon.smithy.rust.codegen.core.smithy.generators.serializationError import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator @@ -135,11 +134,15 @@ open class AwsJson( override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { // TODO We should grep for all of these and move them somewhere central. - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(codegenContext.symbolProvider) fun returnSymbolToParse(shape: Shape): Pair = false to codegenContext.symbolProvider.toSymbol(shape) - return JsonParserGenerator(codegenContext, httpBindingResolver, ::awsJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + return JsonParserGenerator( + codegenContext, + httpBindingResolver, + ::awsJsonFieldName, + builderSymbolFn(codegenContext.symbolProvider), + ::returnSymbolToParse, + ) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index d81b6bc080..dadeb50582 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -35,7 +35,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 6e81d3ca2d..8389ed1e67 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -44,7 +44,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt index 6d2d12bb38..f1387d4f2e 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/AwsQueryParserGeneratorTest.kt @@ -6,12 +6,11 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig @@ -47,14 +46,10 @@ class AwsQueryParserGeneratorTest { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) val symbolProvider = testSymbolProvider(model) - // TODO We should grep for all of these and move them somewhere central. - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(symbolProvider) - val parserGenerator = AwsQueryParserGenerator( testCodegenContext(model), RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - ::builderSymbol, + builderSymbolFn(symbolProvider), ) val operationParser = parserGenerator.operationParser(model.lookup("test#SomeOperation"))!! val project = TestWorkspace.testProject(testSymbolProvider(model)) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt index 3a8fe338de..91b13858a7 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt @@ -6,12 +6,11 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig @@ -48,13 +47,10 @@ class Ec2QueryParserGeneratorTest { // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) - // TODO We should grep for all of these and move them somewhere central. - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(symbolProvider) val parserGenerator = Ec2QueryParserGenerator( testCodegenContext(model), RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - ::builderSymbol, + builderSymbolFn(symbolProvider), ) val operationParser = parserGenerator.operationParser(model.lookup("test#SomeOperation"))!! val project = TestWorkspace.testProject(testSymbolProvider(model)) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt index c4c857f013..b790b38b7f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape @@ -14,7 +13,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol +import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbolFn import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig @@ -96,13 +95,10 @@ internal class XmlBindingTraitParserGeneratorTest { // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) - // TODO We should grep for all of these and move them somewhere central. - fun builderSymbol(shape: StructureShape): Symbol = - shape.builderSymbol(symbolProvider) val parserGenerator = XmlBindingTraitParserGenerator( testCodegenContext(model), RuntimeType.wrappedXmlErrors(TestRuntimeConfig), - ::builderSymbol, + builderSymbolFn(symbolProvider), ) { _, inner -> inner("decoder") } val operationParser = parserGenerator.operationParser(model.lookup("test#Op"))!! val project = TestWorkspace.testProject(testSymbolProvider(model)) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 9ec7248b30..6b8db4c29d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -37,7 +37,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGener import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer @@ -61,6 +60,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.Unconstraine import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.transformers.AttachValidationExceptionToConstrainedOperationInputsInAllowList import software.amazon.smithy.rust.codegen.server.smithy.transformers.RemoveEbsModelValidationException import software.amazon.smithy.rust.codegen.server.smithy.transformers.ShapesReachableFromOperationInputTagger diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 5aec40a9db..4d8a869df4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -21,10 +21,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage /** diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 0b578c1ac3..684d83322f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -17,11 +17,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.validationErrorMessage class MapConstraintViolationGenerator( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index a0be003880..a6c2bedef8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -40,7 +40,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput 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 @@ -49,6 +48,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTraitOrTargetHasConstraintTrait import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWrapperTupleTypeWerePublicConstrainedTypesEnabled /** @@ -92,10 +92,8 @@ class ServerBuilderGenerator( /** * Returns whether a structure shape, whose builder has been generated with [ServerBuilderGenerator], requires a * fallible builder to be constructed. - * - * TODO Rename to `hasFallibleBuilder`, make sure usages are using it as `ServerBuilderGenerator.hasFallibleBuilder`. */ - fun serverHasFallibleBuilder( + fun hasFallibleBuilder( structureShape: StructureShape, model: Model, symbolProvider: SymbolProvider, @@ -123,7 +121,7 @@ class ServerBuilderGenerator( private val structureSymbol = symbolProvider.toSymbol(shape) private val builderSymbol = shape.serverBuilderSymbol(codegenContext) private val moduleName = builderSymbol.namespace.split(builderSymbol.namespaceDelimiter).last() - private val isBuilderFallible = serverHasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) + private val isBuilderFallible = hasFallibleBuilder(shape, model, symbolProvider, takeInUnconstrainedTypes) private val serverBuilderConstraintViolations = ServerBuilderConstraintViolations(codegenContext, shape, takeInUnconstrainedTypes) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt index 54c704b58a..897bdc1166 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorWithoutPublicConstrainedTypes.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -22,7 +23,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext @@ -45,6 +45,23 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( codegenContext: ServerCodegenContext, shape: StructureShape, ) { + companion object { + /** + * Returns whether a structure shape, whose builder has been generated with + * [ServerBuilderGeneratorWithoutPublicConstrainedTypes], requires a fallible builder to be constructed. + * + * This builder only enforces the `required` trait. + */ + fun hasFallibleBuilder( + structureShape: StructureShape, + symbolProvider: SymbolProvider, + ): Boolean = + structureShape + .members() + .map { symbolProvider.toSymbol(it) } + .any { !it.isOptional() } + } + private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val members: List = shape.allMembers.values.toList() @@ -52,8 +69,7 @@ class ServerBuilderGeneratorWithoutPublicConstrainedTypes( private val builderSymbol = shape.serverBuilderSymbol(symbolProvider, false) private val moduleName = builderSymbol.namespace.split("::").last() - private val isBuilderFallible = - StructureGenerator.serverHasFallibleBuilderWithoutPublicConstrainedTypes(shape, symbolProvider) + private val isBuilderFallible = hasFallibleBuilder(shape, symbolProvider) private val serverBuilderConstraintViolations = ServerBuilderConstraintViolations(codegenContext, shape, builderTakesInUnconstrainedTypes = false) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index d14b591241..1514750cda 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -13,11 +13,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput open class ServerEnumGenerator( val codegenContext: ServerCodegenContext, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt index f1782ea360..24435c4a84 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator import software.amazon.smithy.rust.codegen.core.smithy.isOptional -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput /** * Server enums do not have an `Unknown` variant like client enums do, so constructing an enum from @@ -33,7 +33,7 @@ class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat override fun hasFallibleBuilder(shape: StructureShape): Boolean { // Only operation input builders take in unconstrained types. val takesInUnconstrainedTypes = shape.isReachableFromOperationInput() - return ServerBuilderGenerator.serverHasFallibleBuilder( + return ServerBuilderGenerator.hasFallibleBuilder( shape, codegenContext.model, codegenContext.symbolProvider, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt index 52e9f7a9ef..602cbb7aaf 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGenerator.kt @@ -13,10 +13,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput /** * Generates a Rust type for a constrained collection shape that is able to hold values for the corresponding diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt index 842a3b4413..dd470daf9e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedUnionGenerator.kt @@ -25,7 +25,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.smithy.makeRustBoxed import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait -import software.amazon.smithy.rust.codegen.core.smithy.traits.isReachableFromOperationInput 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.toPascalCase @@ -34,6 +33,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput /** * Generates a Rust type for a constrained union shape that is able to hold values for the corresponding _unconstrained_ diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index e5b5c1fd05..4cc16278cb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -767,7 +767,7 @@ private class ServerHttpBoundProtocolTraitImplGenerator( ) } } - val err = if (ServerBuilderGenerator.serverHasFallibleBuilder( + val err = if (ServerBuilderGenerator.hasFallibleBuilder( inputShape, model, symbolProvider, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt similarity index 90% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt index f3e5bd331d..64059fac99 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/traits/ShapeReachableFromOperationInputTagTrait.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.traits +package software.amazon.smithy.rust.codegen.server.smithy.traits import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.CollectionShape @@ -18,12 +18,10 @@ import software.amazon.smithy.model.traits.AnnotationTrait import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.hasTrait -// TODO Move to server. - /** * Tag to indicate that an aggregate shape is reachable from operation input. * - * See the [AggregateShapesReachableFromOperationInputTagger] model transform for how it's used. + * See the [ShapesReachableFromOperationInputTagger] model transform for how it's used. */ class ShapeReachableFromOperationInputTagTrait() : AnnotationTrait(ID, Node.objectNode()) { companion object { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt index 9d63fb74fb..cf58f3f9d9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/ShapesReachableFromOperationInputTagger.kt @@ -13,8 +13,8 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.transform.ModelTransformer -import software.amazon.smithy.rust.codegen.core.smithy.traits.ShapeReachableFromOperationInputTagTrait import software.amazon.smithy.rust.codegen.core.util.UNREACHABLE +import software.amazon.smithy.rust.codegen.server.smithy.traits.ShapeReachableFromOperationInputTagTrait /** * Tag shapes reachable from operation input with the From 51f11b9f3f416a083b83032b60a2c63f78fb1f27 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 24 Oct 2022 17:34:37 +0200 Subject: [PATCH 241/255] Address some TODOs --- .../smithy/generators/ClientInstantiator.kt | 7 ++--- .../core/smithy/generators/Instantiator.kt | 12 ++++---- .../smithy/generators/StructureGenerator.kt | 9 ------ .../generators/http/HttpBindingGenerator.kt | 29 ------------------- .../codegen/core/smithy/protocols/AwsJson.kt | 4 --- .../codegen/core/smithy/protocols/RestJson.kt | 4 +-- .../parse/EventStreamUnmarshallerGenerator.kt | 4 +-- .../protocols/parse/JsonParserGenerator.kt | 11 +------ .../parse/Ec2QueryParserGeneratorTest.kt | 1 - .../parse/JsonParserGeneratorTest.kt | 5 ---- .../XmlBindingTraitParserGeneratorTest.kt | 1 - codegen-server-test/build.gradle.kts | 3 +- .../smithy/generators/ServerInstantiator.kt | 5 ++-- .../http/ServerResponseBindingGenerator.kt | 4 +-- 14 files changed, 19 insertions(+), 80 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt index 4dbadc4f07..629c8a0f93 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -20,14 +20,13 @@ private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writa rust("#T::from($data)", enumSymbol) } -// TODO Move to a separate file. class ClientBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { - override fun hasFallibleBuilder(shape: StructureShape) = + override fun hasFallibleBuilder(shape: StructureShape): Boolean = StructureGenerator.hasFallibleBuilder(shape, codegenContext.symbolProvider) - override fun setterName(memberShape: MemberShape) = memberShape.setterName() + override fun setterName(memberShape: MemberShape): String = memberShape.setterName() - override fun doesSetterTakeInOption(memberShape: MemberShape) = true + override fun doesSetterTakeInOption(memberShape: MemberShape): Boolean = true } fun clientInstantiator(codegenContext: CodegenContext) = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index 952384cbeb..89dc33fb43 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -62,16 +62,14 @@ open class Instantiator( private val symbolProvider: RustSymbolProvider, private val model: Model, private val runtimeConfig: RuntimeConfig, - /** - * TODO Docs - */ + /** Behavior of the builder type used for structure shapes. */ private val builderKindBehavior: BuilderKindBehavior, /** * A function that given a symbol for an enum shape and a string, returns a writable to instantiate the enum with * the string value. **/ private val enumFromStringFn: (Symbol, String) -> Writable, - /** Fill out required fields with a default value **/ + /** Fill out required fields with a default value. **/ private val defaultsForRequiredFields: Boolean = false, ) { data class Ctx( @@ -80,6 +78,10 @@ open class Instantiator( val lowercaseMapKeys: Boolean = false, ) + /** + * Client and server structures have different builder types. `Instantiator` needs to know how the builder + * type behaves to generate code for it. + */ interface BuilderKindBehavior { fun hasFallibleBuilder(shape: StructureShape): Boolean fun setterName(memberShape: MemberShape): String @@ -175,7 +177,7 @@ open class Instantiator( writer.conditionalBlock( "Some(", ")", - // TODO Document the order is important + // The conditions are not commutative: note client builders always take in `Option`. conditional = symbol.isOptional() || (model.expectShape(memberShape.container) is StructureShape && builderKindBehavior.doesSetterTakeInOption(memberShape)), ) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 9c0d7c101e..117ba97838 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -46,15 +46,6 @@ fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, } } -// TODO Is this used? -fun redactIfNecessary(member: MemberShape, model: Model, safeToPrint: String): String { - return if (member.getMemberTrait(model, SensitiveTrait::class.java).isPresent) { - "*** Sensitive Data Redacted ***".dq() - } else { - safeToPrint - } -} - open class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 8a5749a631..78b1419c55 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -154,13 +154,7 @@ class HttpBindingGenerator( fun generateDeserializePrefixHeaderFn(binding: HttpBindingDescriptor): RuntimeType { check(binding.location == HttpBinding.Location.PREFIX_HEADERS) - // TODO Remove -// val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && binding.member.targetCanReachConstrainedShape(model, symbolProvider) val outputSymbol = symbolProvider.toSymbol(binding.member) - // TODO Remove -// if (!returnUnconstrainedType) { -// check(outputSymbol.rustType().stripOuter() is RustType.HashMap) { outputSymbol.rustType() } -// } val target = model.expectShape(binding.member.target) check(target is MapShape) val fnName = "deser_prefix_header_${fnName(operationShape, binding)}" @@ -200,12 +194,6 @@ class HttpBindingGenerator( HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(binding.member), )(this) } - // TODO Remove -// if (returnUnconstrainedType) { -// // If the map shape has constrained string keys or values, we need to wrap the deserialized hash map -// // in the corresponding unconstrained wrapper tuple struct. -// rust("let out = out.map(#T);", outputSymbol.mapRustType { it.stripOuter() }) -// } rust("out.map(Some)") } } @@ -413,7 +401,6 @@ class HttpBindingGenerator( }) """, ) - // TODO Is this arm reachable? Nothing should render to a `HashSet`. is RustType.HashSet -> rust( """ @@ -441,22 +428,6 @@ class HttpBindingGenerator( ) } else { check(targetShape is SimpleShape) - // TODO Remove -// val returnUnconstrainedType = codegenTarget == CodegenTarget.SERVER && -// targetShape is CollectionShape && -// targetShape.canReachConstrainedShape(model, symbolProvider) -// if (returnUnconstrainedType) { -// rust( -// """ -// Ok(if !$parsedValue.is_empty() { -// Some(#T($parsedValue)) -// } else { -// None -// }) -// """, -// symbolProvider.toSymbol(targetShape), -// ) -// } else { rustTemplate( """ if $parsedValue.len() > 1 { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 3056e37e6d..a7c80351f3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -133,15 +133,11 @@ open class AwsJson( listOf("x-amz-target" to "${codegenContext.serviceShape.id.name}.${operationShape.id.name}") override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - // TODO We should grep for all of these and move them somewhere central. - fun returnSymbolToParse(shape: Shape): Pair = - false to codegenContext.symbolProvider.toSymbol(shape) return JsonParserGenerator( codegenContext, httpBindingResolver, ::awsJsonFieldName, builderSymbolFn(codegenContext.symbolProvider), - ::returnSymbolToParse, ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 079b700a26..cbad0029cd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -91,9 +91,7 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) - fun returnSymbolToParse(shape: Shape): Pair = - false to codegenContext.symbolProvider.toSymbol(shape) - return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol, ::returnSymbolToParse) + return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName, ::builderSymbol) } override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index dadeb50582..7b8ab1ae42 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -288,12 +288,13 @@ class EventStreamUnmarshallerGenerator( } private fun RustWriter.renderParseProtocolPayload(member: MemberShape) { + val memberName = symbolProvider.toMemberName(member) val parser = protocol.structuredDataParser(operationShape).payloadParser(member) rustTemplate( """ #{parser}(&message.payload()[..]) .map_err(|err| { - #{Error}::Unmarshalling(format!("failed to unmarshall ${member.memberName}: {}", err)) + #{Error}::Unmarshalling(format!("failed to unmarshall $memberName: {}", err)) })? """, "parser" to parser, @@ -328,7 +329,6 @@ class EventStreamUnmarshallerGenerator( } rust(header) for (member in syntheticUnion.errorMembers) { - // TODO `member.memberName` needs further investigation https://github.com/awslabs/smithy-rs/pull/1342#discussion_r983792584 rustBlock("${member.memberName.dq()} $matchOperator ") { // TODO(EventStream): Errors on the operation can be disjoint with errors in the union, // so we need to generate a new top-level Error type for each event stream union. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 020c0d72b5..640083bf2c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -61,7 +61,6 @@ import software.amazon.smithy.utils.StringUtils * Class describing a JSON parser section that can be used in a customization. */ sealed class JsonParserSection(name: String) : Section(name) { - // TODO Docs data class BeforeBoxingDeserializedMember(val shape: MemberShape) : JsonParserSection("BeforeBoxingDeserializedMember") } @@ -89,7 +88,7 @@ class JsonParserGenerator( * TODO Try to store whether a symbol is unconstrained or not as a property on the `Symbol` itself, and so then * this function should just need to return a `Symbol` as opposed to a pair. */ - private val returnSymbolToParse: (Shape) -> Pair, + private val returnSymbolToParse: (Shape) -> Pair = { shape -> false to codegenContext.symbolProvider.toSymbol(shape) }, private val customizations: List = listOf(), ) : StructuredDataParserGenerator { private val model = codegenContext.model @@ -290,14 +289,6 @@ class JsonParserGenerator( for (customization in customizations) { customization.section(JsonParserSection.BeforeBoxingDeserializedMember(memberShape))(this) } - // TODO Remove -// if (codegenTarget == CodegenTarget.SERVER && -// model.expectShape(memberShape.container).isStructureShape && -// memberShape.targetCanReachConstrainedShape(model, symbolProvider) -// ) { -// // Before boxing, convert into `MaybeConstrained` if the target can reach a constrained shape. -// rust(".map(|x| x.into())") -// } rust(".map(Box::new)") } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt index 91b13858a7..892516b54b 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/Ec2QueryParserGeneratorTest.kt @@ -44,7 +44,6 @@ class Ec2QueryParserGeneratorTest { @Test fun `it modifies operation parsing to include Response and Result tags`() { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) - // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) val parserGenerator = Ec2QueryParserGenerator( diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt index 2d5904d354..fb5eaa6530 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt @@ -116,12 +116,8 @@ class JsonParserGeneratorTest { @Test fun `generates valid deserializers`() { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) - // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) - // TODO We should grep for all of these and move them somewhere central. - fun returnSymbolToParse(shape: Shape): Pair = - false to symbolProvider.toSymbol(shape) fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(symbolProvider) val parserGenerator = JsonParserGenerator( @@ -129,7 +125,6 @@ class JsonParserGeneratorTest { HttpTraitHttpBindingResolver(model, ProtocolContentTypes.consistent("application/json")), ::restJsonFieldName, ::builderSymbol, - ::returnSymbolToParse, ) val operationGenerator = parserGenerator.operationParser(model.lookup("test#Op")) val payloadGenerator = parserGenerator.payloadParser(model.lookup("test#OpOutput\$top")) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt index b790b38b7f..edb9e2a4ab 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGeneratorTest.kt @@ -92,7 +92,6 @@ internal class XmlBindingTraitParserGeneratorTest { @Test fun `generates valid parsers`() { val model = RecursiveShapeBoxer.transform(OperationNormalizer.transform(baseModel)) - // TODO We generate a `testCodegenContext` later on; we should pull out the symbol provider from there. val symbolProvider = testSymbolProvider(model) val parserGenerator = XmlBindingTraitParserGenerator( diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 7c9f8a6c65..21f6ddb194 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -59,8 +59,7 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> imports = listOf("$commonModels/ebs.json"), extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), - // TODO Commented out until https://github.com/awslabs/smithy-rs/issues/1831 is fixed. -// CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), + CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-server-sdk", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt index 24435c4a84..13901e5213 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerInstantiator.kt @@ -28,7 +28,6 @@ private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writa ) } -// TODO Move to a separate file. class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { override fun hasFallibleBuilder(shape: StructureShape): Boolean { // Only operation input builders take in unconstrained types. @@ -41,9 +40,9 @@ class ServerBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat ) } - override fun setterName(memberShape: MemberShape) = codegenContext.symbolProvider.toMemberName(memberShape) + override fun setterName(memberShape: MemberShape): String = codegenContext.symbolProvider.toMemberName(memberShape) - override fun doesSetterTakeInOption(memberShape: MemberShape) = + override fun doesSetterTakeInOption(memberShape: MemberShape): Boolean = codegenContext.symbolProvider.toSymbol(memberShape).isOptional() } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index 7c402ca3b2..020f558ce9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindi import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import software.amazon.smithy.rust.codegen.server.smithy.workingWithPublicConstrainedWrapperTupleType class ServerResponseBindingGenerator( @@ -27,8 +28,7 @@ class ServerResponseBindingGenerator( private val codegenContext: ServerCodegenContext, operationShape: OperationShape, ) { - // TODO Why is this not using serverBuilderSymbol like `ServerRequestBindingGenerator`? - private fun builderSymbol(shape: StructureShape): Symbol = shape.builderSymbol(codegenContext.symbolProvider) + private fun builderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol(codegenContext) private val httpBindingGenerator = HttpBindingGenerator( From 4ec72aa0006e67b5f28dd4a00327d5c5ed032762 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 24 Oct 2022 18:57:46 +0200 Subject: [PATCH 242/255] ./gradlew ktlintFormat --- .../amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt | 2 -- .../smithy/rust/codegen/core/smithy/protocols/RestJson.kt | 1 - .../core/smithy/protocols/serialize/JsonSerializerGenerator.kt | 2 +- .../core/smithy/protocols/parse/JsonParserGeneratorTest.kt | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index a7c80351f3..d527c0bc76 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -5,12 +5,10 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.traits.TimestampFormatTrait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index cbad0029cd..31c5ddae5c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpPayloadTrait import software.amazon.smithy.model.traits.JsonNameTrait diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index b39a7401a4..176453c001 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -57,7 +57,7 @@ sealed class JsonSerializerSection(name: String) : Section(name) { /** Mutate the server error object prior to finalization. Eg: this can be used to inject `__type` to record the error type. */ data class ServerError(val structureShape: StructureShape, val jsonObject: String) : JsonSerializerSection("ServerError") - // TODO Docs + /** Mutate a map prior to it being serialized. **/ data class BeforeIteratingOverMap(val shape: MapShape, val valueExpression: ValueExpression) : JsonSerializerSection("BeforeIteratingOverMap") } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt index fb5eaa6530..7728b5c13f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGeneratorTest.kt @@ -8,7 +8,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse import org.junit.jupiter.api.Test import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape -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.rust.codegen.core.rustlang.RustModule From 3a0a413eb933a1e8af3661dbb1e65cc6769e9816 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 24 Oct 2022 20:07:48 +0200 Subject: [PATCH 243/255] Address some TODOs --- .../smithy/generators/BuilderGenerator.kt | 27 ++++++++++++++++--- .../smithy/generators/StructureGenerator.kt | 19 ------------- .../http/RequestBindingGenerator.kt | 3 ++- .../http/ResponseBindingGenerator.kt | 3 ++- .../protocol/MakeOperationGenerator.kt | 2 ++ .../parse/XmlBindingTraitParserGenerator.kt | 3 ++- .../smithy/generators/InstantiatorTest.kt | 2 +- .../generators/ConstrainedStringGenerator.kt | 1 + 8 files changed, 34 insertions(+), 26 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index aa334d79fe..0dec0b1a65 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators 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.StructureShape @@ -28,16 +29,19 @@ import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.defaultValue import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.toSnakeCase -// TODO This builder generator is only used by the client. -// Move this entire file, and its tests, to `codegen-client`. It's not as easy as it seems. +// TODO(https://github.com/awslabs/smithy-rs/issues/1401) This builder generator is only used by the client. +// Move this entire file, and its tests, to `codegen-client`. fun builderSymbolFn(symbolProvider: RustSymbolProvider): (StructureShape) -> Symbol = { structureShape -> structureShape.builderSymbol(symbolProvider) @@ -72,6 +76,23 @@ class BuilderGenerator( private val symbolProvider: RustSymbolProvider, private val shape: StructureShape, ) { + companion object { + /** + * Returns whether a structure shape, whose builder has been generated with [BuilderGenerator], requires a + * fallible builder to be constructed. + */ + fun hasFallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = + // All operation inputs should have fallible builders in case a new required field is added in the future. + structureShape.hasTrait() || + structureShape + .members() + .map { symbolProvider.toSymbol(it) }.any { + // If any members are not optional && we can't use a default, we need to + // generate a fallible builder. + !it.isOptional() && !it.canUseDefault() + } + } + private val runtimeConfig = symbolProvider.config().runtimeConfig private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) @@ -86,7 +107,7 @@ class BuilderGenerator( } private fun renderBuildFn(implBlockWriter: RustWriter) { - val fallibleBuilder = StructureGenerator.hasFallibleBuilder(shape, symbolProvider) + val fallibleBuilder = hasFallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 117ba97838..6962fd8c23 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -68,25 +68,6 @@ open class StructureGenerator( } } - companion object { - /** - * Returns whether a structure shape, whose builder has been generated with [BuilderGenerator], requires a - * fallible builder to be constructed. - * - * TODO Move this to `BuilderGenerator`. - */ - fun hasFallibleBuilder(structureShape: StructureShape, symbolProvider: SymbolProvider): Boolean = - // All operation inputs should have fallible builders in case a new required field is added in the future. - structureShape.hasTrait() || - structureShape - .members() - .map { symbolProvider.toSymbol(it) }.any { - // If any members are not optional && we can't use a default, we need to - // generate a fallible builder. - !it.isOptional() && !it.canUseDefault() - } - } - /** * Search for lifetimes used by the members of the struct and generate a declaration. * e.g. `<'a, 'b>` diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt index 721ddb1e19..cde95da5f2 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RequestBindingGenerator.kt @@ -48,7 +48,8 @@ fun SmithyPattern.rustFormatString(prefix: String, separator: String): String { return base.dq() } -// TODO Move to `codegen-client` and update docs. `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. +// TODO(https://github.com/awslabs/smithy-rs/issues/1901) Move to `codegen-client` and update docs. +// `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. /** * Generates methods to serialize and deserialize requests based on the HTTP trait. Specifically: * 1. `fn update_http_request(builder: http::request::Builder) -> Builder` diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt index 4c448cda1a..fde74bbd77 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/ResponseBindingGenerator.kt @@ -15,7 +15,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -// TODO Move to `codegen-client` and update docs. `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. +// TODO(https://github.com/awslabs/smithy-rs/issues/1901) Move to `codegen-client` and update docs. +// `MakeOperationGenerator` needs to be moved to `codegen-client` first, which is not easy. class ResponseBindingGenerator( protocol: Protocol, private val codegenContext: CodegenContext, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/MakeOperationGenerator.kt index cda444132c..b374e21b69 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/MakeOperationGenerator.kt @@ -33,6 +33,8 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf +// TODO(https://github.com/awslabs/smithy-rs/issues/1901): Move to `codegen-client`. + /** Generates the `make_operation` function on input structs */ open class MakeOperationGenerator( protected val codegenContext: CodegenContext, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 8389ed1e67..636d39e928 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -42,6 +42,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant @@ -477,7 +478,7 @@ class XmlBindingTraitParserGenerator( rust("let _ = decoder;") } withBlock("Ok(builder.build()", ")") { - if (StructureGenerator.hasFallibleBuilder(shape, symbolProvider)) { + if (BuilderGenerator.hasFallibleBuilder(shape, symbolProvider)) { // NOTE:(rcoh) This branch is unreachable given the current nullability rules. // Only synthetic inputs can have fallible builders, but synthetic inputs can never be parsed // (because they're inputs, only outputs will be parsed!) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt index 7bab81beae..fcbc6e8e3c 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/InstantiatorTest.kt @@ -91,7 +91,7 @@ class InstantiatorTest { // This is the exact same behavior of the client. private class BuilderKindBehavior(val codegenContext: CodegenContext) : Instantiator.BuilderKindBehavior { override fun hasFallibleBuilder(shape: StructureShape) = - StructureGenerator.hasFallibleBuilder(shape, codegenContext.symbolProvider) + BuilderGenerator.hasFallibleBuilder(shape, codegenContext.symbolProvider) override fun setterName(memberShape: MemberShape) = memberShape.setterName() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 4d8a869df4..71bc85c87b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -86,6 +86,7 @@ class ConstrainedStringGenerator( if (constrainedTypeVisibility == Visibility.PUBCRATE) { Attribute.AllowUnused.render(writer) } + // TODO `parse` is usually `&str -> Result`: https://github.com/awslabs/smithy-rs/pull/1342#discussion_r983283531 writer.rustTemplate( """ impl $name { From d7e0aaa724851c0435b4f88ac23e6f9ebc759599 Mon Sep 17 00:00:00 2001 From: david-perez Date: Mon, 24 Oct 2022 20:21:32 +0200 Subject: [PATCH 244/255] ./gradlew ktlintFormat --- .../codegen/client/smithy/generators/ClientInstantiator.kt | 1 - .../client/smithy/protocols/HttpBoundProtocolGenerator.kt | 1 - .../rust/codegen/core/smithy/generators/StructureGenerator.kt | 4 ---- .../smithy/protocols/parse/XmlBindingTraitParserGenerator.kt | 1 - 4 files changed, 7 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt index cc8abc5f98..b74079cc21 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.Instantiator -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName private fun enumFromStringFn(enumSymbol: Symbol, data: String): Writable = writable { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 9bf0bfbc9a..0f1eac1d67 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -27,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.ResponseBindingGenerator diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 6962fd8c23..3972307ac0 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -28,16 +28,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator -import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType -import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, block: Writable) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt index 636d39e928..37b0d1e616 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/XmlBindingTraitParserGenerator.kt @@ -43,7 +43,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName From fb4b5cdc76b3df2a976d6b1655373387a3ac76d0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 25 Oct 2022 14:27:47 +0200 Subject: [PATCH 245/255] Fix Clippy XML serialization of `String` --- .../serialize/XmlBindingTraitSerializerGenerator.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt index 413f98200a..cf8a77a76f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/XmlBindingTraitSerializerGenerator.kt @@ -291,7 +291,14 @@ class XmlBindingTraitSerializerGenerator( private fun RustWriter.serializeRawMember(member: MemberShape, input: String) { when (model.expectShape(member.target)) { is StringShape -> { - rust("$input.as_str()") + // The `input` expression always evaluates to a reference type at this point, but if it does so because + // it's preceded by the `&` operator, calling `as_str()` on it will upset Clippy. + val dereferenced = if (input.startsWith("&")) { + autoDeref(input) + } else { + input + } + rust("$dereferenced.as_str()") } is BooleanShape, is NumberShape -> { rust( From 43803919f3cb1b8605c7056ac90c9f831bd3d4c0 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 26 Oct 2022 19:35:33 +0200 Subject: [PATCH 246/255] Validate operations with constrained input have ValidationException attached --- .../smithy.framework.validation.smithy | 38 ---------- codegen-server-test/build.gradle.kts | 3 +- .../server/smithy/ServerCodegenVisitor.kt | 23 ++++--- ...d.kt => ValidateUnsupportedConstraints.kt} | 69 ++++++++++++++++--- ...ToConstrainedOperationInputsInAllowList.kt | 11 ++- ...ateUnsupportedConstraintsAreNotUsedTest.kt | 32 +++++---- 6 files changed, 102 insertions(+), 74 deletions(-) delete mode 100644 codegen-core/common-test-models/smithy.framework.validation.smithy rename codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/{ValidateUnsupportedConstraintsAreNotUsed.kt => ValidateUnsupportedConstraints.kt} (74%) diff --git a/codegen-core/common-test-models/smithy.framework.validation.smithy b/codegen-core/common-test-models/smithy.framework.validation.smithy deleted file mode 100644 index 156b473ab3..0000000000 --- a/codegen-core/common-test-models/smithy.framework.validation.smithy +++ /dev/null @@ -1,38 +0,0 @@ -// This file is a copy of -// https://github.com/awslabs/smithy/blob/7b2a980c73e87af3214044fe9ca7c391fb269633/smithy-validation-model/model/smithy.framework.validation.smithy - -// TODO This file should be in the classpath. - -$version: "2.0" - -namespace smithy.framework - -/// A standard error for input validation failures. -/// This should be thrown by services when a member of the input structure -/// falls outside of the modeled or documented constraints. -@error("client") -structure ValidationException { - - /// A summary of the validation failure. - @required - message: String, - - /// A list of specific failures encountered while validating the input. - /// A member can appear in this list more than once if it failed to satisfy multiple constraints. - fieldList: ValidationExceptionFieldList -} - -/// Describes one specific validation failure for an input member. -structure ValidationExceptionField { - /// A JSONPointer expression to the structure member whose value failed to satisfy the modeled constraints. - @required - path: String, - - /// A detailed description of the validation failure. - @required - message: String -} - -list ValidationExceptionFieldList { - member: ValidationExceptionField -} diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 21f6ddb194..2cec80f5a2 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion") implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-validation-model:$smithyVersion") } val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> @@ -47,7 +48,7 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, ), CodegenTest("com.amazonaws.constraints#ConstraintsService", "constraints", imports = listOf("$commonModels/constraints.smithy")), - CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json", imports = listOf("$commonModels/smithy.framework.validation.smithy")), + CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy")), CodegenTest("aws.protocoltests.restjson.validation#RestJsonValidation", "rest_json_validation", extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 6b8db4c29d..c0a68a26b7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -189,15 +189,20 @@ open class ServerCodegenVisitor( "[rust-server-codegen] Generating Rust server for service $service, protocol ${codegenContext.protocol}", ) - // TODO We need another validation here that checks that if an operation uses constrained input, it needs to have `ValidationException` attached in `errors`. - - val validationResult = validateUnsupportedConstraintsAreNotUsed(model, service, codegenContext.settings.codegenConfig) - for (logMessage in validationResult.messages) { - // TODO(https://github.com/awslabs/smithy-rs/issues/1756): These are getting duplicated. - logger.log(logMessage.level, logMessage.message) - } - if (validationResult.shouldAbort) { - throw CodegenException("Unsupported constraints used") + for (validationResult in listOf( + validateOperationsWithConstrainedInputHaveValidationExceptionAttached( + model, + service, + ), + validateUnsupportedConstraints(model, service, codegenContext.settings.codegenConfig), + )) { + for (logMessage in validationResult.messages) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1756): These are getting duplicated. + logger.log(logMessage.level, logMessage.message) + } + if (validationResult.shouldAbort) { + throw CodegenException("Unsupported constraints feature used; see error messages above for resolution") + } } val serviceShapes = Walker(model).walkShapes(service) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt similarity index 74% rename from codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index c44ba68468..73c072d52b 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsed.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -4,9 +4,13 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.CollectionShape +import software.amazon.smithy.model.shapes.EnumShape 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.Shape +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait @@ -19,10 +23,11 @@ import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.model.traits.UniqueItemsTrait import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.orNull import java.util.logging.Level -private sealed class MessageKind { +private sealed class UnsupportedConstraintMessageKind { private val constraintTraitsUberIssue = "https://github.com/awslabs/smithy-rs/issues/1401" fun intoLogMessage(ignoreUnsupportedConstraints: Boolean): LogMessage { @@ -87,12 +92,13 @@ private sealed class MessageKind { } } } -private data class UnsupportedConstraintOnMemberShape(val shape: MemberShape, val constraintTrait: Trait) : MessageKind() -private data class UnsupportedConstraintOnShapeReachableViaAnEventStream(val shape: Shape, val constraintTrait: Trait) : MessageKind() -private data class UnsupportedLengthTraitOnStreamingBlobShape(val shape: BlobShape, val lengthTrait: LengthTrait, val streamingTrait: StreamingTrait) : MessageKind() -private data class UnsupportedLengthTraitOnCollectionOrOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait) : MessageKind() -private data class UnsupportedPatternTraitOnStringShape(val shape: Shape, val patternTrait: PatternTrait) : MessageKind() -private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait) : MessageKind() +private data class OperationWithConstrainedInputWithoutValidationException(val shape: OperationShape) +private data class UnsupportedConstraintOnMemberShape(val shape: MemberShape, val constraintTrait: Trait) : UnsupportedConstraintMessageKind() +private data class UnsupportedConstraintOnShapeReachableViaAnEventStream(val shape: Shape, val constraintTrait: Trait) : UnsupportedConstraintMessageKind() +private data class UnsupportedLengthTraitOnStreamingBlobShape(val shape: BlobShape, val lengthTrait: LengthTrait, val streamingTrait: StreamingTrait) : UnsupportedConstraintMessageKind() +private data class UnsupportedLengthTraitOnCollectionOrOnBlobShape(val shape: Shape, val lengthTrait: LengthTrait) : UnsupportedConstraintMessageKind() +private data class UnsupportedPatternTraitOnStringShape(val shape: Shape, val patternTrait: PatternTrait) : UnsupportedConstraintMessageKind() +private data class UnsupportedRangeTraitOnShape(val shape: Shape, val rangeTrait: RangeTrait) : UnsupportedConstraintMessageKind() data class LogMessage(val level: Level, val message: String) data class ValidationResult(val shouldAbort: Boolean, val messages: List) @@ -107,7 +113,50 @@ private val allConstraintTraits = setOf( ) private val unsupportedConstraintsOnMemberShapes = allConstraintTraits - RequiredTrait::class.java -fun validateUnsupportedConstraintsAreNotUsed(model: Model, service: ServiceShape, codegenConfig: ServerCodegenConfig): ValidationResult { +fun validateOperationsWithConstrainedInputHaveValidationExceptionAttached(model: Model, service: ServiceShape): ValidationResult { + // Traverse the model and error out if an operation uses constrained input, but it does not have + // `ValidationException` attached in `errors`. https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809424783 + // TODO(https://github.com/awslabs/smithy-rs/issues/1401): This check will go away once we add support for + // `disableDefaultValidation` set to `true`, allowing service owners to map from constraint violations to operation errors. + val walker = Walker(model) + val operationsWithConstrainedInputWithoutValidationExceptionSet = walker.walkShapes(service) + .filterIsInstance() + .asSequence() + .filter { operationShape -> + // Walk the shapes reachable via this operation input. + walker.walkShapes(operationShape.inputShape(model)) + .any { it is SetShape || it is EnumShape || it.hasConstraintTrait() } + } + .filter { !it.errors.contains(ShapeId.from("smithy.framework#ValidationException")) } + .map { OperationWithConstrainedInputWithoutValidationException(it) } + .toSet() + + val messages = + operationsWithConstrainedInputWithoutValidationExceptionSet.map { + LogMessage( + Level.SEVERE, + """ + Operation ${it.shape.id} takes in input that is constrained + (https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html), and as such can fail with a validation + exception. You must model this behavior in the operation shape in your model file. + """.trimIndent().replace("\n", "") + + """ + ```smithy + use smithy.framework#ValidationException + + operation ${it.shape.id.name} { + ... + errors: [..., ValidationException] // <-- Add this. + } + ``` + """.trimIndent(), + ) + } + + return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) +} + +fun validateUnsupportedConstraints(model: Model, service: ServiceShape, codegenConfig: ServerCodegenConfig): ValidationResult { // Traverse the model and error out if: val walker = Walker(model) @@ -154,7 +203,7 @@ fun validateUnsupportedConstraintsAreNotUsed(model: Model, service: ServiceShape .map { UnsupportedLengthTraitOnCollectionOrOnBlobShape(it, it.expectTrait()) } .toSet() - // 4. Pattern trait on string shapes is used. It has not been implemented yet. + // 5. Pattern trait on string shapes is used. It has not been implemented yet. // TODO(https://github.com/awslabs/smithy-rs/issues/1401) val unsupportedPatternTraitOnStringShapeSet = walker .walkShapes(service) @@ -164,7 +213,7 @@ fun validateUnsupportedConstraintsAreNotUsed(model: Model, service: ServiceShape .map { (shape, patternTrait) -> UnsupportedPatternTraitOnStringShape(shape, patternTrait as PatternTrait) } .toSet() - // 5. Range trait on any shape is used. It has not been implemented yet. + // 6. Range trait on any shape is used. It has not been implemented yet. // TODO(https://github.com/awslabs/smithy-rs/issues/1401) val unsupportedRangeTraitOnShapeSet = walker .walkShapes(service) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt index 67474d3039..257bce1f0e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/transformers/AttachValidationExceptionToConstrainedOperationInputsInAllowList.kt @@ -27,17 +27,26 @@ import software.amazon.smithy.rust.codegen.server.smithy.hasConstraintTrait * * [1]: https://github.com/awslabs/smithy-rs/pull/1199#discussion_r809424783 * - * TODO(https://github.com/awslabs/smithy-rs/issues/1401): This transformer will go away once we implement + * TODO(https://github.com/awslabs/smithy-rs/issues/1401): This transformer will go away once we add support for * `disableDefaultValidation` set to `true`, allowing service owners to map from constraint violations to operation errors. */ object AttachValidationExceptionToConstrainedOperationInputsInAllowList { private val sherviceShapeIdAllowList = setOf( + // These we currently generate server SDKs for. ShapeId.from("aws.protocoltests.restjson#RestJson"), ShapeId.from("aws.protocoltests.json10#JsonRpc10"), ShapeId.from("aws.protocoltests.json#JsonProtocol"), ShapeId.from("com.amazonaws.s3#AmazonS3"), ShapeId.from("com.amazonaws.ebs#Ebs"), + + // These are only loaded in the classpath and need this model transformer, but we don't generate server + // SDKs for them. Here they are for reference. + // ShapeId.from("aws.protocoltests.restxml#RestXml"), + // ShapeId.from("com.amazonaws.glacier#Glacier"), + // ShapeId.from("aws.protocoltests.ec2#AwsEc2"), + // ShapeId.from("aws.protocoltests.query#AwsQuery"), + // ShapeId.from("com.amazonaws.machinelearning#AmazonML_20141212"), ) fun transform(model: Model): Model { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 8b57d5cb1d..f044da17ac 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -8,7 +8,6 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model -import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup @@ -18,7 +17,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { private val baseModel = """ namespace test - + service TestService { version: "123", operations: [TestOperation] @@ -32,28 +31,25 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { private fun validateModel(model: Model, serverCodegenConfig: ServerCodegenConfig = ServerCodegenConfig()): ValidationResult { val service = model.lookup("test#TestService") - return validateUnsupportedConstraintsAreNotUsed(model, service, serverCodegenConfig) + return validateUnsupportedConstraints(model, service, serverCodegenConfig) } @Test - fun `foo`() { + fun `it should detect when an operation with constrained input but that does not have ValidationException attached in errors`() { val model = """ $baseModel structure TestInputOutput { - string: String + @required + requiredString: String } - - @length(min: 1, max: 100) - string LengthString """.asSmithyModel() - val service = model.lookup("test#TestService") - val s = Walker(model).walkShapes(service).toSet() - for (member in s) { - println(member) - } + val validationResult = validateOperationsWithConstrainedInputHaveValidationExceptionAttached(model, service) + + validationResult.messages shouldHaveSize 1 + validationResult.messages[0].message shouldContain "Operation test#TestOperation takes in input that is constrained" } @Test @@ -223,7 +219,10 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { @Test fun `it should not abort when ignoreUnsupportedConstraints is true and unsupported constraints are used`() { - val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true)) + val validationResult = validateModel( + constraintTraitOnStreamingBlobShapeModel, + ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true), + ) validationResult.messages shouldHaveAtLeastSize 1 validationResult.shouldAbort shouldBe false @@ -239,7 +238,10 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { @Test fun `it should set log level to warn when ignoreUnsupportedConstraints is true and unsupported constraints are used`() { - val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true)) + val validationResult = validateModel( + constraintTraitOnStreamingBlobShapeModel, + ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true), + ) validationResult.messages shouldHaveAtLeastSize 1 validationResult.messages.shouldForAll { it.level shouldBe Level.WARNING } From e372bb8799b9c478d8ed40bbb56be439e39b9283 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 26 Oct 2022 20:06:16 +0200 Subject: [PATCH 247/255] Honor `sensitive` in constrained strings --- .../generators/ConstrainedStringGenerator.kt | 5 +- .../ConstrainedStringGeneratorTest.kt | 57 +++++++++++++++++-- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index 71bc85c87b..b8ea4427ad 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput @@ -75,8 +76,6 @@ class ConstrainedStringGenerator( visibility = constrainedTypeVisibility, ) - // TODO Display impl does not honor `sensitive` trait. Implement it on top of https://github.com/awslabs/smithy-rs/pull/1746 - // Note that we're using the linear time check `chars().count()` instead of `len()` on the input value, since the // Smithy specification says the `length` trait counts the number of Unicode code points when applied to string shapes. // https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html#length-trait @@ -123,7 +122,7 @@ class ConstrainedStringGenerator( impl #{Display} for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + ${shape.redactIfNecessary(model, "self.0")}.fmt(f) } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index cd3a961bf8..24320b7fae 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -77,28 +77,28 @@ class ConstrainedStringGeneratorTest { unitTest( name = "parse_success", test = """ - let string = String::from("${testCase.validString}"); + let string = "${testCase.validString}".to_owned(); let _constrained = ConstrainedString::parse(string).unwrap(); """, ) unitTest( name = "try_from_success", test = """ - let string = String::from("${testCase.validString}"); + let string = "${testCase.validString}".to_owned(); let _constrained: ConstrainedString = string.try_into().unwrap(); """, ) unitTest( name = "parse_fail", test = """ - let string = String::from("${testCase.invalidString}"); + let string = "${testCase.invalidString}".to_owned(); let _constrained = ConstrainedString::parse(string).unwrap_err(); """, ) unitTest( name = "try_from_fail", test = """ - let string = String::from("${testCase.invalidString}"); + let string = "${testCase.invalidString}".to_owned(); let constrained_res: Result = string.try_into(); constrained_res.unwrap_err(); """, @@ -106,7 +106,7 @@ class ConstrainedStringGeneratorTest { unitTest( name = "inner", test = """ - let string = String::from("${testCase.validString}"); + let string = "${testCase.validString}".to_owned(); let constrained = ConstrainedString::parse(string).unwrap(); assert_eq!(constrained.inner(), "${testCase.validString}"); @@ -115,7 +115,7 @@ class ConstrainedStringGeneratorTest { unitTest( name = "into_inner", test = """ - let string = String::from("${testCase.validString}"); + let string = "${testCase.validString}".to_owned(); let constrained = ConstrainedString::parse(string.clone()).unwrap(); assert_eq!(constrained.into_inner(), string); @@ -145,4 +145,49 @@ class ConstrainedStringGeneratorTest { // Check that the wrapped type is `pub(crate)`. writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" } + + @Test + fun `Display implementation`() { + val model = """ + namespace test + + @length(min: 1, max: 69) + string ConstrainedString + + @sensitive + @length(min: 1, max: 78) + string SensitiveConstrainedString + """.asSmithyModel() + val constrainedStringShape = model.lookup("test#ConstrainedString") + val sensitiveConstrainedStringShape = model.lookup("test#SensitiveConstrainedString") + + val codegenContext = serverTestCodegenContext(model) + + val project = TestWorkspace.testProject(codegenContext.symbolProvider) + + project.withModule(ModelsModule) { + ConstrainedStringGenerator(codegenContext, this, constrainedStringShape).render() + ConstrainedStringGenerator(codegenContext, this, sensitiveConstrainedStringShape).render() + + unitTest( + name = "non_sensitive_string_display_implementation", + test = """ + let string = "a non-sensitive string".to_owned(); + let constrained = ConstrainedString::parse(string).unwrap(); + assert_eq!(format!("{}", constrained), "a non-sensitive string") + """, + ) + + unitTest( + name = "sensitive_string_display_implementation", + test = """ + let string = "That is how heavy a secret can become. It can make blood flow easier than ink.".to_owned(); + let constrained = SensitiveConstrainedString::parse(string).unwrap(); + assert_eq!(format!("{}", constrained), "*** Sensitive Data Redacted ***") + """, + ) + } + + project.compileAndTest() + } } From 7457edf9bb15fb4f4e44b9a3e41130727ab9a888 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 27 Oct 2022 16:26:08 +0200 Subject: [PATCH 248/255] Remove `parse` method --- .../common-test-models/constraints.smithy | 9 ++++++++ .../generators/ConstrainedMapGenerator.kt | 8 +------ .../ConstrainedShapeGeneratorCommon.kt | 2 +- .../generators/ConstrainedStringGenerator.kt | 7 +----- .../generators/ConstrainedMapGeneratorTest.kt | 18 ++------------- .../ConstrainedStringGeneratorTest.kt | 22 ++++--------------- 6 files changed, 18 insertions(+), 48 deletions(-) diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index 6c1ced2156..d43ea8b7b2 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -45,48 +45,56 @@ operation ConstrainedShapesOperation { operation ConstrainedHttpBoundShapesOperation { input: ConstrainedHttpBoundShapesOperationInputOutput, output: ConstrainedHttpBoundShapesOperationInputOutput, + errors: [ValidationException] } @http(uri: "/constrained-recursive-shapes-operation", method: "POST") operation ConstrainedRecursiveShapesOperation { input: ConstrainedRecursiveShapesOperationInputOutput, output: ConstrainedRecursiveShapesOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-length-map", method: "POST") operation QueryParamsTargetingLengthMapOperation { input: QueryParamsTargetingLengthMapOperationInputOutput, output: QueryParamsTargetingLengthMapOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-map-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfLengthStringOperation { input: QueryParamsTargetingMapOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfLengthStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-map-of-enum-string-operation", method: "POST") operation QueryParamsTargetingMapOfEnumStringOperation { input: QueryParamsTargetingMapOfEnumStringOperationInputOutput, output: QueryParamsTargetingMapOfEnumStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-map-of-list-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfListOfLengthStringOperation { input: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfListOfLengthStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-map-of-set-of-length-string-operation", method: "POST") operation QueryParamsTargetingMapOfSetOfLengthStringOperation { input: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, output: QueryParamsTargetingMapOfSetOfLengthStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/query-params-targeting-map-of-list-of-enum-string-operation", method: "POST") operation QueryParamsTargetingMapOfListOfEnumStringOperation { input: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, output: QueryParamsTargetingMapOfListOfEnumStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/http-prefix-headers-targeting-length-map-operation", method: "POST") @@ -100,6 +108,7 @@ operation HttpPrefixHeadersTargetingLengthMapOperation { operation HttpPrefixHeadersTargetingMapOfEnumStringOperation { input: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, output: HttpPrefixHeadersTargetingMapOfEnumStringOperationInputOutput, + errors: [ValidationException] } @http(uri: "/non-streaming-blob-operation", method: "POST") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt index bc4b2e53b2..677350dd67 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGenerator.kt @@ -84,8 +84,6 @@ class ConstrainedMapGenerator( "ConstraintViolation" to constraintViolation, ) - // TODO(https://github.com/awslabs/smithy-rs/issues/1744): If we end up implementing `Display`, it should honor `sensitive`. - writer.documentShape(shape, model, note = rustDocsNote(name)) constrainedTypeMetadata.render(writer) writer.rustTemplate("struct $name(pub(crate) $inner);", *codegenScope) @@ -95,11 +93,6 @@ class ConstrainedMapGenerator( writer.rustTemplate( """ impl $name { - /// ${rustDocsParseMethod(name, inner)} - pub fn parse(value: $inner) -> Result { - Self::try_from(value) - } - /// ${rustDocsInnerMethod(inner)} pub fn inner(&self) -> &$inner { &self.0 @@ -114,6 +107,7 @@ class ConstrainedMapGenerator( impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; + /// ${rustDocsTryFromMethod(name, inner)} fn try_from(value: $inner) -> Result { let length = value.len(); if $condition { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt index ddf9c81116..d0d447cdad 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedShapeGeneratorCommon.kt @@ -14,7 +14,7 @@ fun rustDocsNote(typeName: String) = "[constraint traits]. Use [`parse`] or [`$typeName::TryFrom`] to construct values of this type." + "[constraint traits]: https://awslabs.github.io/smithy/1.0/spec/core/constraint-traits.html" -fun rustDocsParseMethod(typeName: String, inner: String) = +fun rustDocsTryFromMethod(typeName: String, inner: String) = "Constructs a `$typeName` from an [`$inner`], failing when the provided value does not satisfy the modeled constraints." fun rustDocsInnerMethod(inner: String) = diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt index b8ea4427ad..a63801cce4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGenerator.kt @@ -85,15 +85,9 @@ class ConstrainedStringGenerator( if (constrainedTypeVisibility == Visibility.PUBCRATE) { Attribute.AllowUnused.render(writer) } - // TODO `parse` is usually `&str -> Result`: https://github.com/awslabs/smithy-rs/pull/1342#discussion_r983283531 writer.rustTemplate( """ impl $name { - /// ${rustDocsParseMethod(name, inner)} - pub fn parse(value: $inner) -> Result { - Self::try_from(value) - } - /// Extracts a string slice containing the entire underlying `String`. pub fn as_str(&self) -> &str { &self.0 @@ -129,6 +123,7 @@ class ConstrainedStringGenerator( impl #{TryFrom}<$inner> for $name { type Error = #{ConstraintViolation}; + /// ${rustDocsTryFromMethod(name, inner)} fn try_from(value: $inner) -> Result { let length = value.chars().count(); if $condition { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index fa625c4117..3fa9559943 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -87,13 +87,6 @@ class ConstrainedMapGeneratorTest { instantiator.render(this, constrainedMapShape, testCase.invalidMap) } - unitTest( - name = "parse_success", - test = """ - let map = build_valid_map(); - let _constrained = ConstrainedMap::parse(map).unwrap(); - """, - ) unitTest( name = "try_from_success", test = """ @@ -101,13 +94,6 @@ class ConstrainedMapGeneratorTest { let _constrained: ConstrainedMap = map.try_into().unwrap(); """, ) - unitTest( - name = "parse_fail", - test = """ - let map = build_invalid_map(); - let _constrained = ConstrainedMap::parse(map).unwrap_err(); - """, - ) unitTest( name = "try_from_fail", test = """ @@ -120,7 +106,7 @@ class ConstrainedMapGeneratorTest { name = "inner", test = """ let map = build_valid_map(); - let constrained = ConstrainedMap::parse(map.clone()).unwrap(); + let constrained = ConstrainedMap::try_from(map.clone()).unwrap(); assert_eq!(constrained.inner(), &map); """, @@ -129,7 +115,7 @@ class ConstrainedMapGeneratorTest { name = "into_inner", test = """ let map = build_valid_map(); - let constrained = ConstrainedMap::parse(map.clone()).unwrap(); + let constrained = ConstrainedMap::try_from(map.clone()).unwrap(); assert_eq!(constrained.into_inner(), map); """, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 24320b7fae..75db6303f7 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -74,13 +74,6 @@ class ConstrainedStringGeneratorTest { project.withModule(ModelsModule) { ConstrainedStringGenerator(codegenContext, this, constrainedStringShape).render() - unitTest( - name = "parse_success", - test = """ - let string = "${testCase.validString}".to_owned(); - let _constrained = ConstrainedString::parse(string).unwrap(); - """, - ) unitTest( name = "try_from_success", test = """ @@ -88,13 +81,6 @@ class ConstrainedStringGeneratorTest { let _constrained: ConstrainedString = string.try_into().unwrap(); """, ) - unitTest( - name = "parse_fail", - test = """ - let string = "${testCase.invalidString}".to_owned(); - let _constrained = ConstrainedString::parse(string).unwrap_err(); - """, - ) unitTest( name = "try_from_fail", test = """ @@ -107,7 +93,7 @@ class ConstrainedStringGeneratorTest { name = "inner", test = """ let string = "${testCase.validString}".to_owned(); - let constrained = ConstrainedString::parse(string).unwrap(); + let constrained = ConstrainedString::try_from(string).unwrap(); assert_eq!(constrained.inner(), "${testCase.validString}"); """, @@ -116,7 +102,7 @@ class ConstrainedStringGeneratorTest { name = "into_inner", test = """ let string = "${testCase.validString}".to_owned(); - let constrained = ConstrainedString::parse(string.clone()).unwrap(); + let constrained = ConstrainedString::try_from(string.clone()).unwrap(); assert_eq!(constrained.into_inner(), string); """, @@ -173,7 +159,7 @@ class ConstrainedStringGeneratorTest { name = "non_sensitive_string_display_implementation", test = """ let string = "a non-sensitive string".to_owned(); - let constrained = ConstrainedString::parse(string).unwrap(); + let constrained = ConstrainedString::try_from(string).unwrap(); assert_eq!(format!("{}", constrained), "a non-sensitive string") """, ) @@ -182,7 +168,7 @@ class ConstrainedStringGeneratorTest { name = "sensitive_string_display_implementation", test = """ let string = "That is how heavy a secret can become. It can make blood flow easier than ink.".to_owned(); - let constrained = SensitiveConstrainedString::parse(string).unwrap(); + let constrained = SensitiveConstrainedString::try_from(string).unwrap(); assert_eq!(format!("{}", constrained), "*** Sensitive Data Redacted ***") """, ) From 4bf2cbb9c418d017c645da1ee1ccb5957631d594 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 27 Oct 2022 16:55:18 +0200 Subject: [PATCH 249/255] Copyright header --- .../core/smithy/protocols/parse/JsonParserGenerator.kt | 3 --- codegen-server-test/build.gradle.kts | 3 +-- .../server/smithy/LengthTraitValidationErrorMessage.kt | 5 +++++ .../codegen/server/smithy/ValidateUnsupportedConstraints.kt | 5 +++++ .../BeforeIteratingOverMapJsonCustomization.kt | 5 +++++ .../smithy/generators/ServerBuilderConstraintViolations.kt | 5 +++++ .../codegen/server/smithy/generators/ServerBuilderSymbol.kt | 5 +++++ .../server/smithy/generators/ServerOperationGenerator.kt | 3 ++- .../smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt | 5 +++++ 9 files changed, 33 insertions(+), 6 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 640083bf2c..a10b6b32ae 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -84,9 +84,6 @@ class JsonParserGenerator( * * The function returns a pair where the second component is the return symbol that should be parsed, and the first * component is whether such symbol is unconstrained or not. - * - * TODO Try to store whether a symbol is unconstrained or not as a property on the `Symbol` itself, and so then - * this function should just need to return a `Symbol` as opposed to a pair. */ private val returnSymbolToParse: (Shape) -> Pair = { shape -> false to codegenContext.symbolProvider.toSymbol(shape) }, private val customizations: List = listOf(), diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 2cec80f5a2..e63401daff 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -38,8 +38,7 @@ dependencies { val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> listOf( - // TODO Commented out because we should decide how to fix https://github.com/awslabs/smithy-rs/issues/1826 -// CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), + CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), CodegenTest("naming_obs_structs#NamingObstacleCourseStructs", "naming_test_structs", imports = listOf("$commonModels/naming-obstacle-course-structs.smithy")), CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), CodegenTest( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt index de3fa15ca3..b15b2dc8f0 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/LengthTraitValidationErrorMessage.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.traits.LengthTrait diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index 73c072d52b..4f3d38dcd7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy import software.amazon.smithy.model.Model diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt index 96b3295d28..2a8c17ee48 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/BeforeIteratingOverMapJsonCustomization.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy.customizations import software.amazon.smithy.rust.codegen.core.rustlang.Writable diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt index 3448690a44..f2c572eedc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderConstraintViolations.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.SymbolProvider diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt index 4fa4bc8560..a8ee7fd8f6 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderSymbol.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index b61b1baa45..b34685f7b8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -38,7 +38,8 @@ class ServerOperationGenerator( if (operation.errors.isEmpty()) { rust("std::convert::Infallible") } else { - rust("crate::error::${operationName}Error") + // Name comes from [ServerCombinedErrorGenerator]. + rust("crate::error::${symbolProvider.toSymbol(operation).name}Error") } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index f044da17ac..0624358ba3 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -1,3 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + package software.amazon.smithy.rust.codegen.server.smithy import io.kotest.inspectors.forSome From 7c8c9ff2d45d3dcda906f896f1fda66438c60556 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 27 Oct 2022 22:39:23 +0200 Subject: [PATCH 250/255] rest_json_extras,misc need ValidationException --- codegen-core/common-test-models/misc.smithy | 1 + codegen-core/common-test-models/rest-json-extras.smithy | 2 ++ codegen-core/common-test-models/simple.smithy | 2 +- .../codegen/server/smithy/ValidateUnsupportedConstraints.kt | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/codegen-core/common-test-models/misc.smithy b/codegen-core/common-test-models/misc.smithy index 77cad0f70b..69bdcc2cd8 100644 --- a/codegen-core/common-test-models/misc.smithy +++ b/codegen-core/common-test-models/misc.smithy @@ -238,6 +238,7 @@ operation AcceptHeaderStarService {} operation RequiredHeaderCollectionOperation { input: RequiredHeaderCollectionOperationInputOutput, output: RequiredHeaderCollectionOperationInputOutput, + errors: [ValidationException] } structure RequiredHeaderCollectionOperationInputOutput { diff --git a/codegen-core/common-test-models/rest-json-extras.smithy b/codegen-core/common-test-models/rest-json-extras.smithy index b2c3b2153e..65b7fcc8f1 100644 --- a/codegen-core/common-test-models/rest-json-extras.smithy +++ b/codegen-core/common-test-models/rest-json-extras.smithy @@ -230,6 +230,7 @@ structure MapWithEnumKeyInputOutput { operation MapWithEnumKeyOp { input: MapWithEnumKeyInputOutput, output: MapWithEnumKeyInputOutput, + errors: [ValidationException], } @@ -269,6 +270,7 @@ structure EscapedStringValuesInputOutput { operation EscapedStringValues { input: EscapedStringValuesInputOutput, output: EscapedStringValuesInputOutput, + errors: [ValidationException], } list NonSparseList { diff --git a/codegen-core/common-test-models/simple.smithy b/codegen-core/common-test-models/simple.smithy index 3d574206bf..c685c02f67 100644 --- a/codegen-core/common-test-models/simple.smithy +++ b/codegen-core/common-test-models/simple.smithy @@ -75,7 +75,7 @@ resource Service { operation RegisterService { input: RegisterServiceInputRequest, output: RegisterServiceOutputResponse, - errors: [ResourceAlreadyExists] + errors: [ResourceAlreadyExists, ValidationException] } @documentation("Service register input structure") diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index 4f3d38dcd7..d487689b20 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -141,11 +141,12 @@ fun validateOperationsWithConstrainedInputHaveValidationExceptionAttached(model: LogMessage( Level.SEVERE, """ - Operation ${it.shape.id} takes in input that is constrained + Operation ${it.shape.id} takes in input that is constrained (https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html), and as such can fail with a validation exception. You must model this behavior in the operation shape in your model file. """.trimIndent().replace("\n", "") + """ + ```smithy use smithy.framework#ValidationException From 945e42e7086d6f0a4990c41142fca1e0e0b3ac8b Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 4 Nov 2022 18:07:29 +0100 Subject: [PATCH 251/255] Address jdisanti's comments --- .../core/smithy/generators/Instantiator.kt | 4 ++ .../smithy/generators/error/ErrorGenerator.kt | 9 +++-- .../generators/http/HttpBindingGenerator.kt | 1 - .../protocols/parse/JsonParserGenerator.kt | 37 +++++++++++-------- .../generators/protocol/ServerProtocol.kt | 13 ++++--- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index 89dc33fb43..7973af7603 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -84,6 +84,10 @@ open class Instantiator( */ interface BuilderKindBehavior { fun hasFallibleBuilder(shape: StructureShape): Boolean + + // Client structure builders have two kinds of setters: one that always takes in `Option`, and one that takes + // in the structure field's type. The latter's method name is the field's name, whereas the former is prefixed + // with `set_`. Client instantiators call the `set_*` builder setters. fun setterName(memberShape: MemberShape): String fun doesSetterTakeInOption(memberShape: MemberShape): Boolean } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt index 0cf23afb15..4303fa1a68 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt @@ -90,19 +90,20 @@ class ErrorGenerator( if (messageShape != null) { val messageSymbol = symbolProvider.toSymbol(messageShape).mapRustType { t -> t.asDeref() } val messageType = messageSymbol.rustType() + val memberName = symbolProvider.toMemberName(messageShape) val (returnType, message) = if (messageType.stripOuter() is RustType.Opaque) { // The string shape has a constraint trait that makes its symbol be a wrapper tuple struct. if (messageSymbol.isOptional()) { "Option<&${messageType.stripOuter().render()}>" to - "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" + "self.$memberName.as_ref()" } else { - "&${messageType.render()}" to "&self.${symbolProvider.toMemberName(messageShape)}" + "&${messageType.render()}" to "&self.$memberName" } } else { if (messageSymbol.isOptional()) { - messageType.render() to "self.${symbolProvider.toMemberName(messageShape)}.as_deref()" + messageType.render() to "self.$memberName.as_deref()" } else { - messageType.render() to "self.${symbolProvider.toMemberName(messageShape)}.as_ref()" + messageType.render() to "self.$memberName.as_ref()" } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 78b1419c55..b141e9ec8f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -439,7 +439,6 @@ class HttpBindingGenerator( """, "header_util" to headerUtil, ) -// } } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index a10b6b32ae..a69a8e9651 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -69,6 +69,8 @@ sealed class JsonParserSection(name: String) : Section(name) { */ typealias JsonParserCustomization = NamedSectionGenerator +data class ReturnSymbolToParse(val symbol: Symbol, val isUnconstrained: Boolean) + class JsonParserGenerator( codegenContext: CodegenContext, private val httpBindingResolver: HttpBindingResolver, @@ -82,10 +84,12 @@ class JsonParserGenerator( * user gets. This is only relevant for the server, that parses the incoming request and only after enforces * constraint traits. * - * The function returns a pair where the second component is the return symbol that should be parsed, and the first - * component is whether such symbol is unconstrained or not. + * The function returns a data class that signals the return symbol that should be parsed, and whether it's + * unconstrained or not. */ - private val returnSymbolToParse: (Shape) -> Pair = { shape -> false to codegenContext.symbolProvider.toSymbol(shape) }, + private val returnSymbolToParse: (Shape) -> ReturnSymbolToParse = { shape -> + ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) + }, private val customizations: List = listOf(), ) : StructuredDataParserGenerator { private val model = codegenContext.model @@ -302,7 +306,7 @@ class JsonParserGenerator( withBlock("$escapedStrName.to_unescaped().map(|u|", ")") { when (target.hasTrait()) { true -> { - if (returnSymbolToParse(target).first) { + if (returnSymbolToParse(target).isUnconstrained) { rust("u.into_owned()") } else { rust("#T::from(u.as_ref())", symbolProvider.toSymbol(target)) @@ -353,7 +357,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeCollection(shape: CollectionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val (returnUnconstrainedType, returnSymbol) = returnSymbolToParse(shape) + val (returnSymbol, returnUnconstrainedType) = returnSymbolToParse(shape) val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have lists with names prefixed with `__listOf__`, // which become `__list_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. @@ -404,7 +408,7 @@ class JsonParserGenerator( val keyTarget = model.expectShape(shape.key.target) as StringShape val fnName = symbolProvider.deserializeFunctionName(shape) val isSparse = shape.hasTrait() - val (returnUnconstrainedType, returnSymbol) = returnSymbolToParse(shape) + val returnSymbolToParse = returnSymbolToParse(shape) val parser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { // Allow non-snake-case since some SDK models have maps with names prefixed with `__mapOf__`, // which become `__map_of__`, and the Rust compiler warning doesn't like multiple adjacent underscores. @@ -414,7 +418,7 @@ class JsonParserGenerator( pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "ReturnType" to returnSymbol, + "ReturnType" to returnSymbolToParse.symbol, *codegenScope, ) { startObjectOrNull { @@ -434,8 +438,8 @@ class JsonParserGenerator( } } } - if (returnUnconstrainedType) { - rust("Ok(Some(#{T}(map)))", returnSymbol) + if (returnSymbolToParse.isUnconstrained) { + rust("Ok(Some(#{T}(map)))", returnSymbolToParse.symbol) } else { rust("Ok(Some(map))") } @@ -447,14 +451,14 @@ class JsonParserGenerator( private fun RustWriter.deserializeStruct(shape: StructureShape) { val fnName = symbolProvider.deserializeFunctionName(shape) - val (returnBuilder, returnType) = returnSymbolToParse(shape) + val returnSymbolToParse = returnSymbolToParse(shape) val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { rustBlockTemplate( """ pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> Result, #{Error}> where I: Iterator, #{Error}>> """, - "ReturnType" to returnType, + "ReturnType" to returnSymbolToParse.symbol, *codegenScope, ) { startObjectOrNull { @@ -462,7 +466,7 @@ class JsonParserGenerator( rustTemplate("let mut builder = #{Builder}::default();", *codegenScope, "Builder" to builderSymbol(shape)) deserializeStructInner(shape.members()) // Only call `build()` if the builder is not fallible. Otherwise, return the builder. - if (returnBuilder) { + if (returnSymbolToParse.isUnconstrained) { rust("Ok(Some(builder))") } else { rust("Ok(Some(builder.build()))") @@ -475,7 +479,7 @@ class JsonParserGenerator( private fun RustWriter.deserializeUnion(shape: UnionShape) { val fnName = symbolProvider.deserializeFunctionName(shape) - val (_, symbol) = returnSymbolToParse(shape) + val returnSymbolToParse = returnSymbolToParse(shape) val nestedParser = RuntimeType.forInlineFun(fnName, jsonDeserModule) { rustBlockTemplate( """ @@ -483,7 +487,7 @@ class JsonParserGenerator( where I: Iterator, #{Error}>> """, *codegenScope, - "Shape" to symbol, + "Shape" to returnSymbolToParse.symbol, ) { rust("let mut variant = None;") rustBlock("match tokens.next().transpose()?") { @@ -507,7 +511,7 @@ class JsonParserGenerator( for (member in shape.members()) { val variantName = symbolProvider.toMemberName(member) rustBlock("${jsonName(member).dq()} =>") { - withBlock("Some(#T::$variantName(", "))", symbol) { + withBlock("Some(#T::$variantName(", "))", returnSymbolToParse.symbol) { deserializeMember(member) unwrapOrDefaultOrError(member) } @@ -522,7 +526,8 @@ class JsonParserGenerator( Some(#{Union}::${UnionGenerator.UnknownVariantName}) } """, - "Union" to symbol, *codegenScope, + "Union" to returnSymbolToParse.symbol, + *codegenScope, ) // In server mode, use strict parsing. // Consultation: https://github.com/awslabs/smithy/issues/1222 diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index fdcaa03a79..86b33c617f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldNam import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.ReturnSymbolToParse import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator @@ -106,11 +107,11 @@ class ServerAwsJsonProtocol( override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { fun builderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol(serverCodegenContext) - fun returnSymbolToParse(shape: Shape): Pair = + fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = if (shape.canReachConstrainedShape(codegenContext.model, symbolProvider)) { - true to serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape) + ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) } else { - false to codegenContext.symbolProvider.toSymbol(shape) + ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) } return JsonParserGenerator( codegenContext, @@ -246,11 +247,11 @@ class ServerRestJsonProtocol( override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { fun builderSymbol(shape: StructureShape): Symbol = shape.serverBuilderSymbol(serverCodegenContext) - fun returnSymbolToParse(shape: Shape): Pair = + fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { - true to serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape) + ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) } else { - false to codegenContext.symbolProvider.toSymbol(shape) + ReturnSymbolToParse(serverCodegenContext.symbolProvider.toSymbol(shape), false) } return JsonParserGenerator( codegenContext, From 9f81653bb07dc73239b38ae61d968ff2a449afe6 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 4 Nov 2022 21:04:47 +0100 Subject: [PATCH 252/255] Add changelog entries --- CHANGELOG.next.toml | 124 ++++++++++++++++++ .../generators/ServerBuilderGenerator.kt | 8 +- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 6e34a9093f..c21e86214a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -92,3 +92,127 @@ references = ["smithy-rs#1935"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "jdisanti" +[[smithy-rs]] +message = """ +[Constraint traits](https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html) in server SDKs are beginning to be supported. The following are now supported: + +* The `length` trait on `string` shapes. +* The `length` trait on `map` shapes. + +Upon receiving a request that violates the modeled constraints, the server SDK will reject it with a message indicating why. + +Unsupported (constraint trait, target shape) combinations will now fail at code generation time, whereas previously they were just ignored. This is a breaking change to raise awareness in service owners of their server SDKs behaving differently than what was modeled. To continue generating a server SDK with unsupported constraint traits, set `codegenConfig.ignoreUnsupportedConstraints` to `true` in your `smithy-build.json`. +""" +references = ["smithy-rs#1199", "smithy-rs#1342", "smithy-rs#1401"] +meta = { "breaking" = true, "tada" = true, "bug" = false, "target" = "server" } +author = "david-perez" + +[[smithy-rs]] +message = """ +Server SDKs now generate "constrained types" for constrained shapes. Constrained types are [newtypes](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html) that encapsulate the modeled constraints. They constitute a [widespread pattern to guarantee domain invariants](https://www.lpalmieri.com/posts/2020-12-11-zero-to-production-6-domain-modelling/) and promote correctness in your business logic. So, for example, the model: + +```smithy +@length(min: 1, max: 69) +string NiceString +``` + +will now render a `struct NiceString(String)`. Instantiating a `NiceString` is a fallible operation: + +```rust +let data: String = ... ; +let nice_string = NiceString::try_from(data).expect("data is not nice"); +``` + +A failed attempt to instantiate a constrained type will yield a `ConstraintViolation` error type you may want to handle. This type's API is subject to change. + +Constrained types _guarantee_, by virtue of the type system, that your service's operation outputs adhere to the modeled constraints. To learn more about the motivation for constrained types and how they work, see [the RFC](https://github.com/awslabs/smithy-rs/pull/1199). + +If you'd like to opt-out of generating constrained types, you can set `codegenConfig.publicConstrainedTypes` to `false`. Note that if you do, the generated server SDK will still honor your operation input's modeled constraints upon receiving a request, but will not help you in writing business logic code that adheres to the constraints, and _will not prevent you from returning responses containing operation outputs that violate said constraints_. +""" +references = ["smithy-rs#1342", "smithy-rs#1119"] +meta = { "breaking" = true, "tada" = true, "bug" = false, "target" = "server" } +author = "david-perez" + +[[smithy-rs]] +message = """ +Structure builders in server SDKs have undergone significant changes. + +The API surface has been reduced. It is now simpler and closely follows what you would get when using the [`derive_builder`](https://docs.rs/derive_builder/latest/derive_builder/) crate: + +1. Builders no longer have `set_*` methods taking in `Option`. You must use the unprefixed method, named exactly after the structure's field name, and taking in a value _whose type matches exactly that of the structure's field_. +2. Builders no longer have convenience methods to pass in an element for a field whose type is a vector or a map. You must pass in the entire contents of the collection up front. +3. Builders no longer implement [`PartialEq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html). + +Bug fixes: + +4. Builders now always fail to build if a value for a `required` member is not provided. Previously, builders were falling back to a default value (e.g. `""` for `String`s) for some shapes. This was a bug. + +Additions: + +5. A structure `Structure` with builder `Builder` now implements `TryFrom for Structure` or `From for Structure`, depending on whether the structure [is constrained](https://awslabs.github.io/smithy/2.0/spec/constraint-traits.html) or not, respectively. + +To illustrate how to migrate to the new API, consider the example model below. + +```smithy +structure Pokemon { + @required + name: String, + @required + description: String, + @required + evolvesTo: PokemonList +} + +list PokemonList { + member: Pokemon +} +``` + +In the Rust code below, note the references calling out the changes described in the numbered list above. + +Before: + +```rust +let eevee_builder = Pokemon::builder() + // (1) `set_description` takes in `Some`. + .set_description(Some("Su código genético es muy inestable. Puede evolucionar en diversas razas de Pokémon.".to_owned())) + // (2) Convenience method to add one element to the `evolvesTo` list. + .evolves_to(vaporeon) + .evolves_to(jolteon) + .evolves_to(flareon); + +// (3) Builder types can be compared. +assert_ne!(eevee_builder, Pokemon::builder()); + +// (4) Builds fine even though we didn't provide a value for `name`, which is `required`! +let _eevee = eevee_builder.build(); +``` + +After: + +```rust +let eevee_builder = Pokemon::builder() + // (1) `set_description` no longer exists. Use `description`, which directly takes in `String`. + .description("Su código genético es muy inestable. Puede evolucionar en diversas razas de Pokémon.".to_owned()) + // (2) Convenience methods removed; provide the entire collection up front. + .evolves_to(vec![vaporeon, jolteon, flareon]); + +// (3) Binary operation `==` cannot be applied to `pokemon::Builder`. +// assert_ne!(eevee_builder, Pokemon::builder()); + +// (4) `required` member `name` was not set. +// (5) Builder type can be fallibly converted to the structure using `TryFrom` or `TryInto`. +let _error = Pokemon::try_from(eevee_builder).expect_err("name was not provided"); +``` +""" +references = ["smithy-rs#1714", "smithy-rs#1342"] +meta = { "breaking" = true, "tada" = true, "bug" = true, "target" = "server" } +author = "david-perez" + +[[smithy-rs]] +message = """ +Server SDKs now correctly reject operation inputs that don't set values for `required` structure members. Previously, in some scenarios, server SDKs would accept the request and set a default value for the member (e.g. `""` for a `String`), even when the member shape did not have [Smithy IDL v2's `default` trait](https://awslabs.github.io/smithy/2.0/spec/type-refinement-traits.html#smithy-api-default-trait) attached. The `default` trait is [still unsupported](https://github.com/awslabs/smithy-rs/issues/1860). +""" +references = ["smithy-rs#1714", "smithy-rs#1342", "smithy-rs#1860"] +meta = { "breaking" = true, "tada" = false, "bug" = true, "target" = "server" } +author = "david-perez" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt index a6c2bedef8..6f319f6719 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGenerator.kt @@ -63,17 +63,17 @@ import software.amazon.smithy.rust.codegen.server.smithy.wouldHaveConstrainedWra * These are the main differences with the builders generated by the client's [BuilderGenerator]: * * - The design of this builder is simpler and closely follows what you get when using the [derive_builder] crate: - * * The builder may have one method per struct member named _exactly_ like the struct member and whose input type + * * The builder has one method per struct member named _exactly_ like the struct member and whose input type * matches _exactly_ the struct's member type. This method is generated by [renderBuilderMemberFn]. - * * The builder may have one _setter_ method (i.e. prefixed with `set_`) per struct member whose input type is the + * * The builder has one _setter_ method (i.e. prefixed with `set_`) per struct member whose input type is the * corresponding _unconstrained type_ for the member. This method is always `pub(crate)` and meant for use for * server deserializers only. * * There are no convenience methods to add items to vector and hash map struct members. * - The builder is not `PartialEq`. This is because the builder's members may or may not have been constrained (their - * types hold `MaybeConstrained`, and so it doesn't make sense to compare e.g. two builders holding the same data + * types hold `MaybeConstrained`), and so it doesn't make sense to compare e.g. two builders holding the same data * values, but one builder holds the member in the constrained variant while the other one holds it in the unconstrained * variant. - * - The builder always implement `TryFrom for Structure` or `From for Structure`, depending on whether + * - The builder always implements `TryFrom for Structure` or `From for Structure`, depending on whether * the structure is constrained (and hence enforcing the constraints might yield an error) or not, respectively. * * The builder is `pub(crate)` when `publicConstrainedTypes` is `false`, since in this case the user is never exposed From 655fd1d64e96ed9a1af029f254ff11bb6e00c0cf Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 4 Nov 2022 23:48:35 +0100 Subject: [PATCH 253/255] Bring back failing test and comment I inadvertently deleted in past merge --- .../smithy/generators/protocol/ServerProtocolTestGenerator.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index f6285343fa..781b4ecf9c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -945,6 +945,10 @@ class ServerProtocolTestGenerator( FailingTest(RestJson, "RestJsonMalformedSetNullItem", TestType.MalformedRequest), FailingTest(RestJson, "RestJsonMalformedSetDuplicateBlobs", TestType.MalformedRequest), + FailingTest(RestJson, "RestJsonMalformedUnionNoFieldsSet", TestType.MalformedRequest), + + // Tests involving constraint traits, which are not yet fully implemented. + // See https://github.com/awslabs/smithy-rs/issues/1401. FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthBlobOverride_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedLengthListOverride_case0", TestType.MalformedRequest), From d35394eccdd130c9c357955da71d5c747a16d064 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 15 Nov 2022 14:12:13 +0100 Subject: [PATCH 254/255] Fix EventStreamUnmarshallerGeneratorTest --- .../smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 4fa19feae3..056be65fc4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -273,7 +273,7 @@ class EventStreamUnmarshallerGenerator( rustTemplate( """ std::str::from_utf8(message.payload()) - .map_err(|_| #{Error}::Unmarshalling("message payload is not valid UTF-8".into()))? + .map_err(|_| #{Error}::unmarshalling("message payload is not valid UTF-8"))? .to_owned() """, *codegenScope, From a8301db5636c17ffac8b17f63f8e52ddf0b84b58 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 15 Nov 2022 14:34:28 +0100 Subject: [PATCH 255/255] Kick-off CI again