Unverified Commit 97134ee5 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Refactor generated builder set methods to always take Option (#506)

parent 167e5252
Loading
Loading
Loading
Loading
+25 −24
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ 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.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.asOptional
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.contains
import software.amazon.smithy.rust.codegen.rustlang.documentShape
@@ -187,28 +188,28 @@ class FluentClientGenerator(protocolConfig: ProtocolConfig) {
                        // All fields in the builder are optional
                        val memberSymbol = symbolProvider.toSymbol(member)
                        val outerType = memberSymbol.rustType()
                        val coreType = outerType.stripOuter<RustType.Option>()
                        when (coreType) {
                        when (val coreType = outerType.stripOuter<RustType.Option>()) {
                            is RustType.Vec -> renderVecHelper(member, memberName, coreType)
                            is RustType.HashMap -> renderMapHelper(member, memberName, coreType)
                            else -> {
                                val signature = when (coreType) {
                                    is RustType.String,
                                    is RustType.Box -> "(mut self, inp: impl Into<${coreType.render(true)}>) -> Self"
                                    else -> "(mut self, inp: ${coreType.render(true)}) -> Self"
                                    is RustType.Box -> "(mut self, input: impl Into<${coreType.render(true)}>) -> Self"
                                    else -> "(mut self, input: ${coreType.render(true)}) -> Self"
                                }
                                documentShape(member, model)
                                rustBlock("pub fn $memberName$signature") {
                                    write("self.inner = self.inner.$memberName(inp);")
                                    write("self.inner = self.inner.$memberName(input);")
                                    write("self")
                                }
                            }
                        }
                        // pure setter
                        rustBlock("pub fn ${member.setterName()}(mut self, inp: ${outerType.render(true)}) -> Self") {
                        val inputType = outerType.asOptional()
                        rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") {
                            rust(
                                """
                                self.inner = self.inner.${member.setterName()}(inp);
                                self.inner = self.inner.${member.setterName()}(input);
                                self
                                """
                            )
+6 −0
Original line number Diff line number Diff line
@@ -139,6 +139,12 @@ inline fun <reified T : RustType.Container> RustType.stripOuter(): RustType = wh
    else -> this
}

/** Wraps a type in Option if it isn't already */
fun RustType.asOptional(): RustType = when (this) {
    is RustType.Option -> this
    else -> RustType.Option(this)
}

/**
 * Meta information about a Rust construction (field, struct, or enum)
 */
+14 −15
Original line number Diff line number Diff line
@@ -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.RustType
import software.amazon.smithy.rust.codegen.rustlang.RustWriter
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
@@ -26,7 +27,6 @@ import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.smithy.defaultValue
import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata
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.rustType
import software.amazon.smithy.rust.codegen.util.dq
@@ -125,8 +125,8 @@ class BuilderGenerator(

        fun builderConverter(coreType: RustType) = when (coreType) {
            is RustType.String,
            is RustType.Box -> "inp.into()"
            else -> "inp"
            is RustType.Box -> "input.into()"
            else -> "input"
        }

        writer.rustBlock("impl $builderName") {
@@ -144,8 +144,8 @@ class BuilderGenerator(
                    else -> {
                        val signature = when (coreType) {
                            is RustType.String,
                            is RustType.Box -> "(mut self, inp: impl Into<${coreType.render(true)}>) -> Self"
                            else -> "(mut self, inp: ${coreType.render(true)}) -> Self"
                            is RustType.Box -> "(mut self, input: impl Into<${coreType.render(true)}>) -> Self"
                            else -> "(mut self, input: ${coreType.render(true)}) -> Self"
                        }
                        writer.documentShape(member, model)
                        writer.rustBlock("pub fn $memberName$signature") {
@@ -156,12 +156,10 @@ class BuilderGenerator(
                }

                // Render a `set_foo` method. This is useful as a target for code generation, because the argument type
                // is exactly the same as the resulting member type.
                writer.rustBlock("pub fn ${member.setterName()}(mut self, inp: ${outerType.render(true)}) -> Self") {
                    val v = "inp".letIf(outerType !is RustType.Option) {
                        "Some($it)"
                    }
                    rust("self.$memberName = $v; self")
                // is the same as the resulting member type, and is always optional.
                val inputType = outerType.asOptional()
                writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") {
                    rust("self.$memberName = input; self")
                }
            }
            buildFn(this)
@@ -169,11 +167,11 @@ class BuilderGenerator(
    }

    private fun RustWriter.renderVecHelper(memberName: String, coreType: RustType.Vec) {
        rustBlock("pub fn $memberName(mut self, inp: impl Into<${coreType.member.render(true)}>) -> Self") {
        rustBlock("pub fn $memberName(mut self, input: impl Into<${coreType.member.render(true)}>) -> Self") {
            rust(
                """
                let mut v = self.$memberName.unwrap_or_default();
                v.push(inp.into());
                v.push(input.into());
                self.$memberName = Some(v);
                self
                """
@@ -210,6 +208,7 @@ class BuilderGenerator(
     *    field3: builder.field3.unwrap_or_default()
     *    field4: builder.field4.ok_or("field4 is required when building SomeStruct")?
     * }
     * ```
     */
    protected fun coreBuilder(writer: RustWriter) {
        writer.rustBlock("#T", structureSymbol) {
+5 −4
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ 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.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.asOptional
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.contains
import software.amazon.smithy.rust.codegen.rustlang.documentShape
@@ -306,8 +307,7 @@ class FluentClientGenerator(protocolConfig: ProtocolConfig) {
                        // All fields in the builder are optional
                        val memberSymbol = symbolProvider.toSymbol(member)
                        val outerType = memberSymbol.rustType()
                        val coreType = outerType.stripOuter<RustType.Option>()
                        when (coreType) {
                        when (val coreType = outerType.stripOuter<RustType.Option>()) {
                            is RustType.Vec -> renderVecHelper(member, memberName, coreType)
                            is RustType.HashMap -> renderMapHelper(member, memberName, coreType)
                            else -> {
@@ -324,10 +324,11 @@ class FluentClientGenerator(protocolConfig: ProtocolConfig) {
                            }
                        }
                        // pure setter
                        rustBlock("pub fn ${member.setterName()}(mut self, inp: ${outerType.render(true)}) -> Self") {
                        val inputType = outerType.asOptional()
                        rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") {
                            rust(
                                """
                                self.inner = self.inner.${member.setterName()}(inp);
                                self.inner = self.inner.${member.setterName()}(input);
                                self
                                """
                            )
+21 −23
Original line number Diff line number Diff line
@@ -58,20 +58,24 @@ class Instantiator(
    private val model: Model,
    private val runtimeConfig: RuntimeConfig
) {

    data class Ctx(
        // The Rust HTTP library lower cases headers but Smithy protocol tests
        // contain httpPrefix headers with uppercase keys
    data class Ctx(val lowercaseMapKeys: Boolean, val streaming: Boolean)
        val lowercaseMapKeys: Boolean,
        val streaming: Boolean,
        // Whether or not we are instantiating with a Builder, in which case all setters take Option
        val builder: Boolean
    )

    fun render(
        writer: RustWriter,
        shape: Shape,
        arg: Node,
        ctx: Ctx = Ctx(lowercaseMapKeys = false, streaming = false)
        ctx: Ctx = Ctx(lowercaseMapKeys = false, streaming = false, builder = false)
    ) {
        when (shape) {
            // Compound Shapes
            is StructureShape -> renderStructure(writer, shape, arg as ObjectNode, ctx)
            is StructureShape -> renderStructure(writer, shape, arg as ObjectNode, ctx.copy(builder = true))
            is UnionShape -> renderUnion(writer, shape, arg as ObjectNode, ctx)

            // Collections
@@ -135,7 +139,7 @@ class Instantiator(
            ) { "A null node was provided for $shape but the symbol was not optional. This is invalid input data." }
            writer.write("None")
        } else {
            writer.conditionalBlock("Some(", ")", conditional = symbol.isOptional()) {
            writer.conditionalBlock("Some(", ")", conditional = ctx.builder || symbol.isOptional()) {
                writer.conditionalBlock(
                    "Box::new(",
                    ")",
@@ -145,11 +149,10 @@ class Instantiator(
                        this,
                        target,
                        arg,
                        ctx.letIf(shape.getMemberTrait(model, HttpPrefixHeadersTrait::class.java).isPresent) {
                        ctx.copy(builder = false)
                            .letIf(shape.getMemberTrait(model, HttpPrefixHeadersTrait::class.java).isPresent) {
                                it.copy(lowercaseMapKeys = true)
                        }.letIf(
                            shape.isStreaming(model)
                        ) {
                            }.letIf(shape.isStreaming(model)) {
                                it.copy(streaming = true)
                            }
                    )
@@ -245,12 +248,7 @@ class Instantiator(
        data.members.forEach { (key, value) ->
            val memberShape = shape.expectMember(key.value)
            writer.withBlock(".${memberShape.setterName()}(", ")") {
                renderMember(
                    this,
                    memberShape,
                    value,
                    ctx
                )
                renderMember(this, memberShape, value, ctx)
            }
        }
        writer.write(".build()")
Loading