Unverified Commit 5239e9c5 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Extract builderInstantiator interface to prepare for nullability changes (#2988)

## Motivation and Context
#1725 exposed the need for easily configuring builder behavior between
client & server

## Description
- extract builderGenerator interface and attach it to `codegenContext`
to avoid loads of threading it up and down

## Testing
- codegen unit tests
- [x] codegen diff audit

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS
SDK, generated SDK code, or SDK runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 9b0e3cfd
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -9,10 +9,12 @@ 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.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientBuilderInstantiator
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.ModuleDocProvider
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderInstantiator
import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol

/**
@@ -36,4 +38,7 @@ data class ClientCodegenContext(
    model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT,
) {
    val enableUserConfigurableRuntimePlugins: Boolean get() = settings.codegenConfig.enableUserConfigurableRuntimePlugins
    override fun builderInstantiator(): BuilderInstantiator {
        return ClientBuilderInstantiator(symbolProvider)
    }
}
+41 −0
Original line number Diff line number Diff line
/*
 * 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.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.map
import software.amazon.smithy.rust.codegen.core.rustlang.rust
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.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator
import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderInstantiator

fun ClientCodegenContext.builderInstantiator(): BuilderInstantiator = ClientBuilderInstantiator(symbolProvider)

class ClientBuilderInstantiator(private val symbolProvider: RustSymbolProvider) : BuilderInstantiator {
    override fun setField(builder: String, value: Writable, field: MemberShape): Writable {
        return setFieldWithSetter(builder, value, field)
    }

    override fun finalizeBuilder(builder: String, shape: StructureShape, mapErr: Writable?): Writable = writable {
        if (BuilderGenerator.hasFallibleBuilder(shape, symbolProvider)) {
            rustTemplate(
                "$builder.build()#{mapErr}?",
                "mapErr" to (
                    mapErr?.map {
                        rust(".map_err(#T)", it)
                    } ?: writable { }
                    ),
            )
        } else {
            rust("$builder.build()")
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -63,9 +63,9 @@ private class ClientAwsJsonFactory(private val version: AwsJsonVersion) :
    ProtocolGeneratorFactory<OperationGenerator, ClientCodegenContext> {
    override fun protocol(codegenContext: ClientCodegenContext): Protocol =
        if (compatibleWithAwsQuery(codegenContext.serviceShape, version)) {
            AwsQueryCompatible(codegenContext, AwsJson(codegenContext, version))
            AwsQueryCompatible(codegenContext, AwsJson(codegenContext, version, codegenContext.builderInstantiator()))
        } else {
            AwsJson(codegenContext, version)
            AwsJson(codegenContext, version, codegenContext.builderInstantiator())
        }

    override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): OperationGenerator =
+4 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.core.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.core.smithy.generators.BuilderInstantiator

/**
 * [CodegenContext] contains code-generation context that is _common to all_  smithy-rs plugins.
@@ -17,7 +18,7 @@ import software.amazon.smithy.model.shapes.ShapeId
 * If your data is specific to the `rust-client-codegen` client plugin, put it in [ClientCodegenContext] instead.
 * If your data is specific to the `rust-server-codegen` server plugin, put it in [ServerCodegenContext] instead.
 */
open class CodegenContext(
abstract class CodegenContext(
    /**
     * The smithy model.
     *
@@ -89,4 +90,6 @@ open class CodegenContext(
    fun expectModuleDocProvider(): ModuleDocProvider = checkNotNull(moduleDocProvider) {
        "A ModuleDocProvider must be set on the CodegenContext"
    }

    abstract fun builderInstantiator(): BuilderInstantiator
}
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.rust.codegen.core.smithy.generators

import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable

/** Abstraction for instantiating builders.
 *
 * Builder abstractions vary—clients MAY use `build_with_error_correction`, e.g., and builders can vary in fallibility.
 * */
interface BuilderInstantiator {
    /** Set a field on a builder. */
    fun setField(builder: String, value: Writable, field: MemberShape): Writable

    /** Finalize a builder, turning it into a built object
     *  - In the case of builders-of-builders, the value should be returned directly
     *  - If an error is returned, you MUST use `mapErr` to convert the error type
     */
    fun finalizeBuilder(builder: String, shape: StructureShape, mapErr: Writable? = null): Writable

    /** Set a field on a builder using the `$setterName` method. $value will be passed directly. */
    fun setFieldWithSetter(builder: String, value: Writable, field: MemberShape) = writable {
        rustTemplate("$builder = $builder.${field.setterName()}(#{value})", "value" to value)
    }
}
Loading