Unverified Commit 5d6d8843 authored by david-perez's avatar david-perez Committed by GitHub
Browse files

Split `CodegenContext` and children (#1473)

This commit splits each of:

* `CodegenContext`,
* `RustSettings`; and
* `CodegenConfig`

into two variants: one for the `rust-codegen` client plugin and another
for the `rust-server-codegen` plugin.

(`CodegenConfig` is a property of `RustSettings`, which is a property of
`CodegenContext`; they constitute pervasive read-only global data that
gets passed around to the code generators).

This allows each plugin to evolve separately, defining different
settings and passing around a tailor-made code-generation context.

Client-specific generators should now use:

* `ClientCodegenContext`,
* `ClientRustSettings`; and
* `ClientCodegenConfig`.

Likewise, server-specific generators should now use:

* `ServerCodegenContext`,
* `ServerRustSettings`; and
* `ServerCodegenConfig`.

This is a more robust and maintainable approach than the current one,
where both generator types have to read possibly null values (because
they don't make sense for a client/server target), and plugins have to
populate settings that are not relevant for the crate they're
generating. For example, the server plugin has to populate and pass
around the settings `renameExceptions`, `includeFluentClient` and
`addMessageToErrors` even though it never uses them and don't make sense
for it.

As of writing these client and server specific classes are very similar,
but in the future they will evolve in separate ways (for example, #1342
will add symbol providers for constrained shapes that only make sense in
a server code-generation context).

Common properties have not been duplicated however. Instead, utilizing
inheritance, code-generation context that is common to _all_ plugins can
be placed in:

* `CoreCodegenContext`,
* `CoreRustSettings`; and
* `CoreCodegenConfig`.

This way, generators that are shared by both client and server can take
in `CoreCodegenContext`. If they need to tweak the code they generate in
small and self-contained places, they can utilize the `CodegenTarget`
flag-like enum to disambiguate, like they have always been doing.

Note this change also makes `RustDecorator`s be targeted for clients or
servers, by making them generic over the code-generation context. When
loading the decorators from the classpath and combining them into one in
the executed smithy-rs plugin, all of them need to either target clients
or target servers. This makes sense: after all, all of the decorators
that conform the AWS Rust SDK are client-specific.

Closes #828.
parent 1446681c
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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,7 @@ val DECORATORS = listOf(
    DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true))
)

class AwsCodegenDecorator : CombinedCodegenDecorator(DECORATORS) {
class AwsCodegenDecorator : CombinedCodegenDecorator<ClientCodegenContext>(DECORATORS) {
    override val name: String = "AwsSdkCodegenDecorator"
    override val order: Byte = -1
}
+13 −12
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@ 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.CodegenContext
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
@@ -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<ClientCodegenContext> {
    override val name: String = "AwsEndpoint"
    override val order: Byte = 0

@@ -46,14 +47,14 @@ class AwsEndpointDecorator : RustCodegenDecorator {
    }

    override fun configCustomizations(
        codegenContext: CodegenContext,
        codegenContext: ClientCodegenContext,
        baseCustomizations: List<ConfigCustomization>
    ): List<ConfigCustomization> {
        return baseCustomizations + EndpointConfigCustomization(codegenContext, endpoints)
    }

    override fun operationCustomizations(
        codegenContext: CodegenContext,
        codegenContext: ClientCodegenContext,
        operation: OperationShape,
        baseCustomizations: List<OperationCustomization>
    ): List<OperationCustomization> {
@@ -61,18 +62,18 @@ class AwsEndpointDecorator : RustCodegenDecorator {
    }

    override fun libRsCustomizations(
        codegenContext: CodegenContext,
        codegenContext: ClientCodegenContext,
        baseCustomizations: List<LibRsCustomization>
    ): List<LibRsCustomization> {
        return baseCustomizations + PubUseEndpoint(codegenContext.runtimeConfig)
    }
}

class EndpointConfigCustomization(private val codegenContext: CodegenContext, private val endpointData: ObjectNode) :
class EndpointConfigCustomization(private val coreCodegenContext: CoreCodegenContext, private val endpointData: ObjectNode) :
    ConfigCustomization() {
    private val runtimeConfig = codegenContext.runtimeConfig
    private val runtimeConfig = coreCodegenContext.runtimeConfig
    private val resolveAwsEndpoint = runtimeConfig.awsEndpoint().asType().copy(name = "ResolveAwsEndpoint")
    private val moduleUseName = codegenContext.moduleUseName()
    private val moduleUseName = coreCodegenContext.moduleUseName()
    override fun section(section: ServiceConfig): Writable = writable {
        when (section) {
            is ServiceConfig.ConfigStruct -> rust(
@@ -116,7 +117,7 @@ class EndpointConfigCustomization(private val codegenContext: CodegenContext, pr
                    "aws_types" to awsTypes(runtimeConfig).asType()
                )
            ServiceConfig.BuilderBuild -> {
                val resolverGenerator = EndpointResolverGenerator(codegenContext, endpointData)
                val resolverGenerator = EndpointResolverGenerator(coreCodegenContext, endpointData)
                rust(
                    """
                    endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||
@@ -163,9 +164,9 @@ class PubUseEndpoint(private val runtimeConfig: RuntimeConfig) : LibRsCustomizat
    }
}

class EndpointResolverGenerator(codegenContext: CodegenContext, private val endpointData: ObjectNode) {
    private val runtimeConfig = codegenContext.runtimeConfig
    private val endpointPrefix = codegenContext.serviceShape.expectTrait<ServiceTrait>().endpointPrefix
class EndpointResolverGenerator(coreCodegenContext: CoreCodegenContext, private val endpointData: ObjectNode) {
    private val runtimeConfig = coreCodegenContext.runtimeConfig
    private val endpointPrefix = coreCodegenContext.serviceShape.expectTrait<ServiceTrait>().endpointPrefix
    private val awsEndpoint = runtimeConfig.awsEndpoint().asType()
    private val awsTypes = runtimeConfig.awsTypes().asType()
    private val codegenScope =
+10 −9
Original line number Diff line number Diff line
@@ -19,7 +19,8 @@ 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.CodegenContext
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.RustCrate
@@ -72,13 +73,13 @@ 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<ClientCodegenContext> {
    override val name: String = "FluentClient"

    // Must run after the AwsPresigningDecorator so that the presignable trait is correctly added to operations
    override val order: Byte = (AwsPresigningDecorator.ORDER + 1).toByte()

    override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) {
    override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) {
        val types = Types(codegenContext.runtimeConfig)
        FluentClientGenerator(
            codegenContext,
@@ -97,7 +98,7 @@ class AwsFluentClientDecorator : RustCodegenDecorator {
    }

    override fun libRsCustomizations(
        codegenContext: CodegenContext,
        codegenContext: ClientCodegenContext,
        baseCustomizations: List<LibRsCustomization>
    ): List<LibRsCustomization> {
        return baseCustomizations + object : LibRsCustomization() {
@@ -181,12 +182,12 @@ private class AwsFluentClientExtensions(types: Types) {
    }
}

private class AwsFluentClientDocs(codegenContext: CodegenContext) : FluentClientCustomization() {
    private val serviceName = codegenContext.serviceShape.expectTrait<TitleTrait>().value
    private val serviceShape = codegenContext.serviceShape
    private val crateName = codegenContext.moduleUseName()
private class AwsFluentClientDocs(coreCodegenContext: CoreCodegenContext) : FluentClientCustomization() {
    private val serviceName = coreCodegenContext.serviceShape.expectTrait<TitleTrait>().value
    private val serviceShape = coreCodegenContext.serviceShape
    private val crateName = coreCodegenContext.moduleUseName()
    private val codegenScope =
        arrayOf("aws_config" to codegenContext.runtimeConfig.awsConfig().copy(scope = DependencyScope.Dev).asType())
        arrayOf("aws_config" to coreCodegenContext.runtimeConfig.awsConfig().copy(scope = DependencyScope.Dev).asType())

    // Usage docs on STS must be suppressed—aws-config cannot be added as a dev-dependency because it would create
    // a circular dependency
+10 −9
Original line number Diff line number Diff line
@@ -27,7 +27,8 @@ 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.CodegenContext
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.customize.OperationCustomization
import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection
@@ -84,7 +85,7 @@ internal val PRESIGNABLE_OPERATIONS by lazy {

class AwsPresigningDecorator internal constructor(
    private val presignableOperations: Map<ShapeId, PresignableOperation> = PRESIGNABLE_OPERATIONS
) : RustCodegenDecorator {
) : RustCodegenDecorator<ClientCodegenContext> {
    companion object {
        const val ORDER: Byte = 0
    }
@@ -93,7 +94,7 @@ class AwsPresigningDecorator internal constructor(
    override val order: Byte = ORDER

    override fun operationCustomizations(
        codegenContext: CodegenContext,
        codegenContext: ClientCodegenContext,
        operation: OperationShape,
        baseCustomizations: List<OperationCustomization>
    ): List<OperationCustomization> = baseCustomizations + listOf(AwsInputPresignedMethod(codegenContext, operation))
@@ -129,11 +130,11 @@ class AwsPresigningDecorator internal constructor(
}

class AwsInputPresignedMethod(
    private val codegenContext: CodegenContext,
    private val coreCodegenContext: CoreCodegenContext,
    private val operationShape: OperationShape
) : OperationCustomization() {
    private val runtimeConfig = codegenContext.runtimeConfig
    private val symbolProvider = codegenContext.symbolProvider
    private val runtimeConfig = coreCodegenContext.runtimeConfig
    private val symbolProvider = coreCodegenContext.symbolProvider

    private val codegenScope = arrayOf(
        "Error" to AwsRuntimeType.Presigning.member("config::Error"),
@@ -158,7 +159,7 @@ class AwsInputPresignedMethod(
        val presignableOp = PRESIGNABLE_OPERATIONS.getValue(operationShape.id)

        val makeOperationOp = if (presignableOp.hasModelTransforms()) {
            codegenContext.model.expectShape(syntheticShapeId(operationShape.id), OperationShape::class.java)
            coreCodegenContext.model.expectShape(syntheticShapeId(operationShape.id), OperationShape::class.java)
        } else {
            section.operationShape
        }
@@ -166,9 +167,9 @@ class AwsInputPresignedMethod(

        val protocol = section.protocol
        MakeOperationGenerator(
            codegenContext,
            coreCodegenContext,
            protocol,
            HttpBoundProtocolPayloadGenerator(codegenContext, protocol),
            HttpBoundProtocolPayloadGenerator(coreCodegenContext, protocol),
            // Prefixed with underscore to avoid colliding with modeled functions
            functionName = makeOperationFn,
            public = false,
+4 −5
Original line number Diff line number Diff line
@@ -10,12 +10,11 @@ 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.CodegenContext
import software.amazon.smithy.rust.codegen.smithy.ClientCodegenContext
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,16 +25,16 @@ 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<ClientCodegenContext> {
    override val name: String = "AwsReadmeDecorator"
    override val order: Byte = 0

    private val logger: Logger = Logger.getLogger(javaClass.name)

    override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations =
    override fun crateManifestCustomizations(codegenContext: ClientCodegenContext): ManifestCustomizations =
        mapOf("package" to mapOf("readme" to "README.md"))

    override fun extras(codegenContext: CodegenContext, rustCrate: RustCrate) {
    override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) {
        rustCrate.withFile("README.md") { writer ->
            val description = normalizeDescription(
                codegenContext.moduleName,
Loading