Loading aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/FluentClientGenerator.kt +25 −24 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 """ ) Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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) */ Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +14 −15 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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") { Loading @@ -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") { Loading @@ -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) Loading @@ -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 """ Loading Loading @@ -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) { Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 -> { Loading @@ -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 """ ) Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +21 −23 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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(", ")", Loading @@ -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) } ) Loading Loading @@ -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 Loading
aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/FluentClientGenerator.kt +25 −24 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 """ ) Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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) */ Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +14 −15 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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") { Loading @@ -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") { Loading @@ -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) Loading @@ -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 """ Loading Loading @@ -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) { Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt +5 −4 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 -> { Loading @@ -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 """ ) Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/Instantiator.kt +21 −23 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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(", ")", Loading @@ -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) } ) Loading Loading @@ -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