Unverified Commit 15eb1c08 authored by Zelda Hessler's avatar Zelda Hessler Committed by GitHub
Browse files

add: links to related types for fluent builders (#1022)

* add: links to related types for fluent builders

* update: CHANGELOG.next.toml

* update: generate fancy documented lists

* refactor: put docs on client builder methods instead of fluent builder
rename: generateShapeMemberList -> generateShapeMemberDocs
refactor: move generateShapeMemberList to FluentClientDecorator.kt
update: appease spellchecker

* remove: extra line in docs

* remove: unnecessary snake case conversion
parent f1329022
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -10,3 +10,9 @@
# references = ["smithy-rs#920"]
# meta = { "breaking" = false, "tada" = false, "bug" = false }
# author = "rcoh"

[[aws-sdk-rust]]
message = "The docs for fluent builders now have easy links to their corresponding Input, Output, and Error structs"
references = ["aws-sdk-rust#348"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "Velfi"
+3 −3
Original line number Diff line number Diff line
@@ -231,7 +231,7 @@ fun <T : CodeWriter> T.documentShape(shape: Shape, model: Model, autoSuppressMis
    return this
}

/** Document the containing entity (eg. module, crate, etc.)
/** Document the containing entity (e.g. module, crate, etc.)
 * Instead of prefixing lines with `///` lines are prefixed with `//!`
 */
fun RustWriter.containerDocs(text: String, vararg args: Any): RustWriter {
@@ -357,8 +357,8 @@ class RustWriter private constructor(
        return "${prefix}_$n"
    }

    fun first(prewriter: RustWriter.() -> Unit) {
        preamble.add(prewriter)
    fun first(preWriter: RustWriter.() -> Unit) {
        preamble.add(preWriter)
    }

    /**
+62 −7
Original line number Diff line number Diff line
@@ -5,10 +5,13 @@

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
import software.amazon.smithy.model.traits.DocumentationTrait
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.Feature
import software.amazon.smithy.rust.codegen.rustlang.RustMetadata
@@ -22,6 +25,9 @@ import software.amazon.smithy.rust.codegen.rustlang.asOptional
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.docs
import software.amazon.smithy.rust.codegen.rustlang.documentShape
import software.amazon.smithy.rust.codegen.rustlang.escape
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
@@ -41,6 +47,7 @@ import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata
import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol
import software.amazon.smithy.rust.codegen.smithy.rustType
import software.amazon.smithy.rust.codegen.util.inputShape
import software.amazon.smithy.rust.codegen.util.orNull
import software.amazon.smithy.rust.codegen.util.outputShape
import software.amazon.smithy.rust.codegen.util.toSnakeCase

@@ -370,12 +377,45 @@ class FluentClientGenerator(
                val maybePaginated = if (operation.isPaginated(model)) {
                    "\n/// This operation supports pagination. See [`into_paginator()`]($fullPath::into_paginator)."
                } else ""
                rust(

                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") {
                    "///   - $it"
                }

                var inputFieldsHead = "/// - Takes [`${operationInput.name}`]($operationInput)"
                if (inputFieldsBody.isNotEmpty()) {
                    inputFieldsHead += " with field(s):"
                }

                val outputFieldsBody = generateShapeMemberDocs(writer, symbolProvider, output, model).joinToString("\n") {
                    "///   - $it"
                }

                var outputFieldsHead = "/// - On success, responds with [`${operationOk.name}`]($operationOk)"
                if (outputFieldsBody.isNotEmpty()) {
                    outputFieldsHead += " with field(s):"
                }

                rustTemplate(
                    """
                    /// Constructs a fluent builder for the `$name` operation.
                    /// Constructs a fluent builder for the [`$name`]($fullPath) operation.$maybePaginated
                    ///
                    /// See [`$name`]($fullPath) for more information about the
                    /// operation and its arguments.$maybePaginated
                    $inputFieldsHead
                    $inputFieldsBody
                    $outputFieldsHead
                    $outputFieldsBody
                    /// - On failure, responds with [`SdkError<${operationErr.name}>`]($operationErr)
                    """
                )

                rust(
                    """
                    pub fn ${
                    clientOperationFnName(
                        operation,
@@ -402,7 +442,6 @@ class FluentClientGenerator(
            operations.forEach { operation ->
                val operationSymbol = symbolProvider.toSymbol(operation)
                val input = operation.inputShape(model)
                val members: List<MemberShape> = input.allMembers.values.toList()
                val baseDerives = symbolProvider.toSymbol(input).expectRustMetadata().derives
                val derives = baseDerives.derives.intersect(setOf(RuntimeType.Clone)) + RuntimeType.Debug
                rust(
@@ -411,6 +450,7 @@ class FluentClientGenerator(
                    ///
                    """
                )

                documentShape(operation, model, autoSuppressMissingDocs = false)
                baseDerives.copy(derives = derives).render(this)
                rustTemplate(
@@ -487,7 +527,7 @@ class FluentClientGenerator(
                            operation.errorSymbol(symbolProvider)
                        )
                    )
                    members.forEach { member ->
                    input.allMembers.values.forEach { member ->
                        val memberName = symbolProvider.toMemberName(member)
                        // All fields in the builder are optional
                        val memberSymbol = symbolProvider.toSymbol(member)
@@ -522,3 +562,18 @@ class FluentClientGenerator(
        }
    }
}

fun generateShapeMemberDocs(writer: RustWriter, symbolProvider: SymbolProvider, shape: StructureShape, model: Model): List<String> {
    val structName = symbolProvider.toSymbol(shape).rustType().qualifiedName()
    return shape.members().map { memberShape ->
        val name = symbolProvider.toMemberName(memberShape)
        val member = symbolProvider.toSymbol(memberShape).rustType().render(fullyQualified = false)
        val docTrait = memberShape.getMemberTrait(model, DocumentationTrait::class.java).orNull()
        val docs = when (docTrait?.value?.isNotBlank()) {
            true -> normalizeHtml(writer.escape(docTrait.value)).replace("\n", " ")
            else -> "(undocumented)"
        }

        "[`$name($member)`]($structName::$name): $docs"
    }
}