Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +27 −10 Original line number Diff line number Diff line Loading @@ -133,22 +133,39 @@ fun RustType.qualifiedName(): String { return "$namespace$name" } /** Format this Rust type as an `impl Into<T>` */ fun RustType.implInto(fullyQualified: Boolean = true): String { return "impl Into<${this.render(fullyQualified)}>" } fun RustType.asArgument(name: String): Argument { /** Format this Rust type so that it may be used as an argument type in a function definition */ fun RustType.asArgumentType(fullyQualified: Boolean = true): String { return when (this) { is RustType.String, is RustType.Box -> Argument( "$name: ${this.implInto()}", "$name.into()", ) else -> Argument( "$name: ${this.render()}", name, ) is RustType.Box -> this.implInto(fullyQualified) else -> this.render(fullyQualified) } } /** 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()" else -> name } } /** * For a given name, generate an `Argument` data class containing pre-formatted strings for using this type when * writing a Rust function */ fun RustType.asArgument(name: String): Argument { return Argument( "$name: ${this.asArgumentType()}", this.asArgumentValue(name), this.render(), ) } /** Loading Loading @@ -364,4 +381,4 @@ sealed class Attribute { } } data class Argument(val argument: String, val value: String) data class Argument(val argument: String, val value: String, val type: String) codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientCore.kt +24 −14 Original line number Diff line number Diff line Loading @@ -16,40 +16,50 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock class FluentClientCore(private val model: Model) { /** Generate and write Rust code for a builder method that sets a Vec<T> */ fun RustWriter.renderVecHelper(member: MemberShape, memberName: String, coreType: RustType.Vec) { docs("Appends an item to `${member.memberName}`.") rust("///") docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model) val input = coreType.member.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { rust( """ self.inner = self.inner.$memberName(${input.value}); self """ ) write("self.inner = self.inner.$memberName(${input.value});") write("self") } } /** Generate and write Rust code for a builder method that sets a HashMap<K,V> */ fun RustWriter.renderMapHelper(member: MemberShape, memberName: String, coreType: RustType.HashMap) { docs("Adds a key-value pair to `${member.memberName}`.") rust("///") docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model) val k = coreType.key.asArgument("k") val v = coreType.member.asArgument("v") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${k.argument}, ${v.argument}) -> Self") { rust( """ self.inner = self.inner.$memberName(${k.value}, ${v.value}); self """ ) write("self.inner = self.inner.$memberName(${k.value}, ${v.value});") write("self") } } /** * Generate and write Rust code for a builder method that sets an input. Can be used for setter methods as well e.g. * * `renderInputHelper(memberShape, "foo", RustType.String)` -> `pub fn foo(mut self, input: impl Into<String>) -> Self { ... }` * `renderInputHelper(memberShape, "set_bar", RustType.Option)` -> `pub fn set_bar(mut self, input: Option<String>) -> Self { ... }` */ fun RustWriter.renderInputHelper(member: MemberShape, memberName: String, coreType: RustType) { val functionInput = coreType.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${functionInput.argument}) -> Self") { write("self.inner = self.inner.$memberName(${functionInput.value});") write("self") } } } codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt +76 −33 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.smithy.generators import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.TopDownIndex 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.StructureShape Loading @@ -20,7 +21,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.Writable import software.amazon.smithy.rust.codegen.rustlang.asArgument import software.amazon.smithy.rust.codegen.rustlang.asArgumentType import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.docLink Loading @@ -31,7 +32,6 @@ import software.amazon.smithy.rust.codegen.rustlang.normalizeHtml import software.amazon.smithy.rust.codegen.rustlang.qualifiedName 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 Loading Loading @@ -374,31 +374,30 @@ class FluentClientGenerator( ) { operations.forEach { operation -> val name = symbolProvider.toSymbol(operation).name val fullPath = "crate::client::fluent_builders::$name" val fullPath = operation.fullyQualifiedFluentBuilder(symbolProvider) val maybePaginated = if (operation.isPaginated(model)) { "\n/// This operation supports pagination. See [`into_paginator()`]($fullPath::into_paginator)." "\n/// This operation supports pagination; See [`into_paginator()`]($fullPath::into_paginator)." } else "" val input = operation.inputShape(model) val output = operation.outputShape(model) val operationInput = symbolProvider.toSymbol(input) val operationOk = symbolProvider.toSymbol(output) val operationErr = operation.errorSymbol(symbolProvider).toSymbol() val inputFieldsBody = generateShapeMemberDocs(writer, symbolProvider, input, model).joinToString("\n") { val inputFieldsBody = generateOperationShapeDocs(writer, symbolProvider, operation, model).joinToString("\n") { "/// - $it" } var inputFieldsHead = "/// - Takes [`${operationInput.name}`]($operationInput)" if (inputFieldsBody.isNotEmpty()) { inputFieldsHead += " with field(s):" val inputFieldsHead = if (inputFieldsBody.isNotEmpty()) { "The fluent builder is configurable:" } else { "The fluent builder takes no input, just [`send`]($fullPath::send) it." } val outputFieldsBody = generateShapeMemberDocs(writer, symbolProvider, output, model).joinToString("\n") { "/// - $it" } var outputFieldsHead = "/// - On success, responds with [`${operationOk.name}`]($operationOk)" var outputFieldsHead = "On success, responds with [`${operationOk.name}`]($operationOk)" if (outputFieldsBody.isNotEmpty()) { outputFieldsHead += " with field(s):" } Loading @@ -407,9 +406,9 @@ class FluentClientGenerator( """ /// Constructs a fluent builder for the [`$name`]($fullPath) operation.$maybePaginated /// $inputFieldsHead /// - $inputFieldsHead $inputFieldsBody $outputFieldsHead /// - $outputFieldsHead $outputFieldsBody /// - On failure, responds with [`SdkError<${operationErr.name}>`]($operationErr) """ Loading Loading @@ -528,7 +527,7 @@ class FluentClientGenerator( operation.errorSymbol(symbolProvider) ) ) input.allMembers.values.forEach { member -> input.members().forEach { member -> val memberName = symbolProvider.toMemberName(member) // All fields in the builder are optional val memberSymbol = symbolProvider.toSymbol(member) Loading @@ -536,34 +535,46 @@ class FluentClientGenerator( when (val coreType = outerType.stripOuter<RustType.Option>()) { is RustType.Vec -> with(core) { renderVecHelper(member, memberName, coreType) } is RustType.HashMap -> with(core) { renderMapHelper(member, memberName, coreType) } else -> { val functionInput = coreType.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${functionInput.argument}) -> Self") { write("self.inner = self.inner.$memberName(${functionInput.value});") write("self") else -> with(core) { renderInputHelper(member, memberName, coreType) } } // pure setter val setterName = member.setterName() val optionalInputType = outerType.asOptional() with(core) { renderInputHelper(member, setterName, optionalInputType) } } } // pure setter val inputType = outerType.asOptional() documentShape(member, model) rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") { rust( """ self.inner = self.inner.${member.setterName()}(input); self """ ) } } } } /** * For a given `operation` shape, return a list of strings where each string describes the name and input type of one of * the operation's corresponding fluent builder methods as well as that method's documentation from the smithy model */ fun generateOperationShapeDocs(writer: RustWriter, symbolProvider: SymbolProvider, operation: OperationShape, model: Model): List<String> { val input = operation.inputShape(model) val fluentBuilderFullyQualifiedName = operation.fullyQualifiedFluentBuilder(symbolProvider) return input.members().map { member -> val builderInputDoc = member.asFluentBuilderInputDoc(symbolProvider) val builderInputLink = "$fluentBuilderFullyQualifiedName::${symbolProvider.toMemberName(member)}" val builderSetterDoc = member.asFluentBuilderSetterDoc(symbolProvider) val builderSetterLink = "$fluentBuilderFullyQualifiedName::${member.setterName()}" val docTrait = member.getMemberTrait(model, DocumentationTrait::class.java).orNull() val docs = when (docTrait?.value?.isNotBlank()) { true -> normalizeHtml(writer.escape(docTrait.value)).replace("\n", " ") else -> "(undocumented)" } "[`$builderInputDoc`]($builderInputLink) / [`$builderSetterDoc`]($builderSetterLink): $docs" } } /** * For a give `struct` shape, return a list of strings where each string describes the name and type of a struct field * as well as that field's documentation from the smithy model */ fun generateShapeMemberDocs(writer: RustWriter, symbolProvider: SymbolProvider, shape: StructureShape, model: Model): List<String> { val structName = symbolProvider.toSymbol(shape).rustType().qualifiedName() return shape.members().map { memberShape -> Loading @@ -578,3 +589,35 @@ fun generateShapeMemberDocs(writer: RustWriter, symbolProvider: SymbolProvider, "[`$name($member)`](${docLink("$structName::$name")}): $docs" } } /** * Generate a valid fully-qualified Type for a fluent builder e.g. * `OperationShape(AssumeRole)` -> `"crate::client::fluent_builders::AssumeRole"` */ fun OperationShape.fullyQualifiedFluentBuilder(symbolProvider: SymbolProvider): String { val operationName = symbolProvider.toSymbol(this).name return "crate::client::fluent_builders::$operationName" } /** * Generate a string that looks like a Rust function pointer for documenting a fluent builder method e.g. * `<MemberShape representing a struct method>` -> `"method_name(MethodInputType)"` */ fun MemberShape.asFluentBuilderInputDoc(symbolProvider: SymbolProvider): String { val memberName = symbolProvider.toMemberName(this) val outerType = symbolProvider.toSymbol(this).rustType() return "$memberName(${outerType.stripOuter<RustType.Option>().asArgumentType(fullyQualified = false)})" } /** * Generate a string that looks like a Rust function pointer for documenting a fluent builder setter method e.g. * `<MemberShape representing a struct method>` -> `"set_method_name(Option<MethodInputType>)"` */ fun MemberShape.asFluentBuilderSetterDoc(symbolProvider: SymbolProvider): String { val memberName = this.setterName() val outerType = symbolProvider.toSymbol(this).rustType() return "$memberName(${outerType.asArgumentType(fullyQualified = false)})" } Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +27 −10 Original line number Diff line number Diff line Loading @@ -133,22 +133,39 @@ fun RustType.qualifiedName(): String { return "$namespace$name" } /** Format this Rust type as an `impl Into<T>` */ fun RustType.implInto(fullyQualified: Boolean = true): String { return "impl Into<${this.render(fullyQualified)}>" } fun RustType.asArgument(name: String): Argument { /** Format this Rust type so that it may be used as an argument type in a function definition */ fun RustType.asArgumentType(fullyQualified: Boolean = true): String { return when (this) { is RustType.String, is RustType.Box -> Argument( "$name: ${this.implInto()}", "$name.into()", ) else -> Argument( "$name: ${this.render()}", name, ) is RustType.Box -> this.implInto(fullyQualified) else -> this.render(fullyQualified) } } /** 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()" else -> name } } /** * For a given name, generate an `Argument` data class containing pre-formatted strings for using this type when * writing a Rust function */ fun RustType.asArgument(name: String): Argument { return Argument( "$name: ${this.asArgumentType()}", this.asArgumentValue(name), this.render(), ) } /** Loading Loading @@ -364,4 +381,4 @@ sealed class Attribute { } } data class Argument(val argument: String, val value: String) data class Argument(val argument: String, val value: String, val type: String)
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientCore.kt +24 −14 Original line number Diff line number Diff line Loading @@ -16,40 +16,50 @@ import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock class FluentClientCore(private val model: Model) { /** Generate and write Rust code for a builder method that sets a Vec<T> */ fun RustWriter.renderVecHelper(member: MemberShape, memberName: String, coreType: RustType.Vec) { docs("Appends an item to `${member.memberName}`.") rust("///") docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model) val input = coreType.member.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { rust( """ self.inner = self.inner.$memberName(${input.value}); self """ ) write("self.inner = self.inner.$memberName(${input.value});") write("self") } } /** Generate and write Rust code for a builder method that sets a HashMap<K,V> */ fun RustWriter.renderMapHelper(member: MemberShape, memberName: String, coreType: RustType.HashMap) { docs("Adds a key-value pair to `${member.memberName}`.") rust("///") docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model) val k = coreType.key.asArgument("k") val v = coreType.member.asArgument("v") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${k.argument}, ${v.argument}) -> Self") { rust( """ self.inner = self.inner.$memberName(${k.value}, ${v.value}); self """ ) write("self.inner = self.inner.$memberName(${k.value}, ${v.value});") write("self") } } /** * Generate and write Rust code for a builder method that sets an input. Can be used for setter methods as well e.g. * * `renderInputHelper(memberShape, "foo", RustType.String)` -> `pub fn foo(mut self, input: impl Into<String>) -> Self { ... }` * `renderInputHelper(memberShape, "set_bar", RustType.Option)` -> `pub fn set_bar(mut self, input: Option<String>) -> Self { ... }` */ fun RustWriter.renderInputHelper(member: MemberShape, memberName: String, coreType: RustType) { val functionInput = coreType.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${functionInput.argument}) -> Self") { write("self.inner = self.inner.$memberName(${functionInput.value});") write("self") } } }
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt +76 −33 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.smithy.generators import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.TopDownIndex 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.StructureShape Loading @@ -20,7 +21,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.Writable import software.amazon.smithy.rust.codegen.rustlang.asArgument import software.amazon.smithy.rust.codegen.rustlang.asArgumentType import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.asType import software.amazon.smithy.rust.codegen.rustlang.docLink Loading @@ -31,7 +32,6 @@ import software.amazon.smithy.rust.codegen.rustlang.normalizeHtml import software.amazon.smithy.rust.codegen.rustlang.qualifiedName 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 Loading Loading @@ -374,31 +374,30 @@ class FluentClientGenerator( ) { operations.forEach { operation -> val name = symbolProvider.toSymbol(operation).name val fullPath = "crate::client::fluent_builders::$name" val fullPath = operation.fullyQualifiedFluentBuilder(symbolProvider) val maybePaginated = if (operation.isPaginated(model)) { "\n/// This operation supports pagination. See [`into_paginator()`]($fullPath::into_paginator)." "\n/// This operation supports pagination; See [`into_paginator()`]($fullPath::into_paginator)." } else "" val input = operation.inputShape(model) val output = operation.outputShape(model) val operationInput = symbolProvider.toSymbol(input) val operationOk = symbolProvider.toSymbol(output) val operationErr = operation.errorSymbol(symbolProvider).toSymbol() val inputFieldsBody = generateShapeMemberDocs(writer, symbolProvider, input, model).joinToString("\n") { val inputFieldsBody = generateOperationShapeDocs(writer, symbolProvider, operation, model).joinToString("\n") { "/// - $it" } var inputFieldsHead = "/// - Takes [`${operationInput.name}`]($operationInput)" if (inputFieldsBody.isNotEmpty()) { inputFieldsHead += " with field(s):" val inputFieldsHead = if (inputFieldsBody.isNotEmpty()) { "The fluent builder is configurable:" } else { "The fluent builder takes no input, just [`send`]($fullPath::send) it." } val outputFieldsBody = generateShapeMemberDocs(writer, symbolProvider, output, model).joinToString("\n") { "/// - $it" } var outputFieldsHead = "/// - On success, responds with [`${operationOk.name}`]($operationOk)" var outputFieldsHead = "On success, responds with [`${operationOk.name}`]($operationOk)" if (outputFieldsBody.isNotEmpty()) { outputFieldsHead += " with field(s):" } Loading @@ -407,9 +406,9 @@ class FluentClientGenerator( """ /// Constructs a fluent builder for the [`$name`]($fullPath) operation.$maybePaginated /// $inputFieldsHead /// - $inputFieldsHead $inputFieldsBody $outputFieldsHead /// - $outputFieldsHead $outputFieldsBody /// - On failure, responds with [`SdkError<${operationErr.name}>`]($operationErr) """ Loading Loading @@ -528,7 +527,7 @@ class FluentClientGenerator( operation.errorSymbol(symbolProvider) ) ) input.allMembers.values.forEach { member -> input.members().forEach { member -> val memberName = symbolProvider.toMemberName(member) // All fields in the builder are optional val memberSymbol = symbolProvider.toSymbol(member) Loading @@ -536,34 +535,46 @@ class FluentClientGenerator( when (val coreType = outerType.stripOuter<RustType.Option>()) { is RustType.Vec -> with(core) { renderVecHelper(member, memberName, coreType) } is RustType.HashMap -> with(core) { renderMapHelper(member, memberName, coreType) } else -> { val functionInput = coreType.asArgument("input") documentShape(member, model) rustBlock("pub fn $memberName(mut self, ${functionInput.argument}) -> Self") { write("self.inner = self.inner.$memberName(${functionInput.value});") write("self") else -> with(core) { renderInputHelper(member, memberName, coreType) } } // pure setter val setterName = member.setterName() val optionalInputType = outerType.asOptional() with(core) { renderInputHelper(member, setterName, optionalInputType) } } } // pure setter val inputType = outerType.asOptional() documentShape(member, model) rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") { rust( """ self.inner = self.inner.${member.setterName()}(input); self """ ) } } } } /** * For a given `operation` shape, return a list of strings where each string describes the name and input type of one of * the operation's corresponding fluent builder methods as well as that method's documentation from the smithy model */ fun generateOperationShapeDocs(writer: RustWriter, symbolProvider: SymbolProvider, operation: OperationShape, model: Model): List<String> { val input = operation.inputShape(model) val fluentBuilderFullyQualifiedName = operation.fullyQualifiedFluentBuilder(symbolProvider) return input.members().map { member -> val builderInputDoc = member.asFluentBuilderInputDoc(symbolProvider) val builderInputLink = "$fluentBuilderFullyQualifiedName::${symbolProvider.toMemberName(member)}" val builderSetterDoc = member.asFluentBuilderSetterDoc(symbolProvider) val builderSetterLink = "$fluentBuilderFullyQualifiedName::${member.setterName()}" val docTrait = member.getMemberTrait(model, DocumentationTrait::class.java).orNull() val docs = when (docTrait?.value?.isNotBlank()) { true -> normalizeHtml(writer.escape(docTrait.value)).replace("\n", " ") else -> "(undocumented)" } "[`$builderInputDoc`]($builderInputLink) / [`$builderSetterDoc`]($builderSetterLink): $docs" } } /** * For a give `struct` shape, return a list of strings where each string describes the name and type of a struct field * as well as that field's documentation from the smithy model */ fun generateShapeMemberDocs(writer: RustWriter, symbolProvider: SymbolProvider, shape: StructureShape, model: Model): List<String> { val structName = symbolProvider.toSymbol(shape).rustType().qualifiedName() return shape.members().map { memberShape -> Loading @@ -578,3 +589,35 @@ fun generateShapeMemberDocs(writer: RustWriter, symbolProvider: SymbolProvider, "[`$name($member)`](${docLink("$structName::$name")}): $docs" } } /** * Generate a valid fully-qualified Type for a fluent builder e.g. * `OperationShape(AssumeRole)` -> `"crate::client::fluent_builders::AssumeRole"` */ fun OperationShape.fullyQualifiedFluentBuilder(symbolProvider: SymbolProvider): String { val operationName = symbolProvider.toSymbol(this).name return "crate::client::fluent_builders::$operationName" } /** * Generate a string that looks like a Rust function pointer for documenting a fluent builder method e.g. * `<MemberShape representing a struct method>` -> `"method_name(MethodInputType)"` */ fun MemberShape.asFluentBuilderInputDoc(symbolProvider: SymbolProvider): String { val memberName = symbolProvider.toMemberName(this) val outerType = symbolProvider.toSymbol(this).rustType() return "$memberName(${outerType.stripOuter<RustType.Option>().asArgumentType(fullyQualified = false)})" } /** * Generate a string that looks like a Rust function pointer for documenting a fluent builder setter method e.g. * `<MemberShape representing a struct method>` -> `"set_method_name(Option<MethodInputType>)"` */ fun MemberShape.asFluentBuilderSetterDoc(symbolProvider: SymbolProvider): String { val memberName = this.setterName() val outerType = symbolProvider.toSymbol(this).rustType() return "$memberName(${outerType.asArgumentType(fullyQualified = false)})" }