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

Improve generated docs for crates and clients (#898)

* Improve generated docs for crates and clients

This commit makes a first pass at improving the documentation rendered for AWS crates for both generated crates as well as `Client` structs.

* Fixups

* CR feedback
parent 722f3135
Loading
Loading
Loading
Loading
+71 −1
Original line number Diff line number Diff line
@@ -5,12 +5,16 @@

package software.amazon.smithy.rustsdk

import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.TitleTrait
import software.amazon.smithy.rust.codegen.rustlang.Attribute
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.DependencyScope
import software.amazon.smithy.rust.codegen.rustlang.Feature
import software.amazon.smithy.rust.codegen.rustlang.RustMetadata
import software.amazon.smithy.rust.codegen.rustlang.RustModule
import software.amazon.smithy.rust.codegen.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.Writable
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.rust
import software.amazon.smithy.rust.codegen.rustlang.rustBlock
@@ -22,9 +26,12 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.smithy.RustCrate
import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.smithy.generators.ClientGenerics
import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientCustomization
import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientGenerator
import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientSection
import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization
import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection
import software.amazon.smithy.rust.codegen.util.expectTrait

private class Types(runtimeConfig: RuntimeConfig) {
    private val smithyClientDep = CargoDependency.SmithyClient(runtimeConfig).copy(optional = true)
@@ -67,7 +74,10 @@ class AwsFluentClientDecorator : RustCodegenDecorator {
                        "AwsFluentClient_retry" to types.smithyClientRetry,
                    )
                ),
                customizations = listOf(AwsPresignedFluentBuilderMethod(codegenContext.runtimeConfig))
                customizations = listOf(
                    AwsPresignedFluentBuilderMethod(codegenContext.runtimeConfig),
                    AwsFluentClientDocs(codegenContext)
                )
            ).render(writer)
            AwsFluentClientExtensions(types).render(writer)
        }
@@ -143,3 +153,63 @@ private class AwsFluentClientExtensions(private val types: Types) {
        }
    }
}

private class AwsFluentClientDocs(codegenContext: CodegenContext) : FluentClientCustomization() {
    private val serviceName = codegenContext.serviceShape.expectTrait<TitleTrait>().value
    private val serviceShape = codegenContext.serviceShape
    private val crateName = codegenContext.moduleUseName()
    private val codegenScope =
        arrayOf("aws_config" to codegenContext.runtimeConfig.awsConfig().copy(scope = DependencyScope.Dev).asType())

    // Usage docs on STS must be suppressed—aws-config cannot be added as a dev-dependency because it would create
    // a circular dependency
    private fun suppressUsageDocs(): Boolean =
        serviceShape.id == ShapeId.from("com.amazonaws.sts#AWSSecurityTokenServiceV20110615")

    override fun section(section: FluentClientSection): Writable {
        return when (section) {
            is FluentClientSection.FluentClientDocs -> writable {
                rustTemplate(
                    """
                    /// Client for $serviceName
                    ///
                    /// Client for invoking operations on $serviceName. Each operation on $serviceName is a method on this
                    /// this struct. `.send()` MUST be invoked on the generated operations to dispatch the request to the service."""
                )
                if (!suppressUsageDocs()) {
                    rustTemplate(
                        """
                        ///
                        /// ## Examples
                        /// **Constructing a client and invoking an operation**
                        /// ```rust,no_run
                        /// ## async fn docs() {
                        ///     // create a shared configuration. This can be used & shared between multiple service clients.
                        ///     let shared_config = #{aws_config}::load_from_env().await;
                        ///     let client = $crateName::Client::new(&shared_config);
                        ///     // invoke an operation
                        ///     /* let rsp = client
                        ///         .<operationname>().
                        ///         .<param>("some value")
                        ///         .send().await; */
                        /// ## }
                        /// ```
                        /// **Constructing a client with custom configuration**
                        /// ```rust,no_run
                        /// use #{aws_config}::RetryConfig;
                        /// ## async fn docs() {
                        ///     let shared_config = #{aws_config}::load_from_env().await;
                        ///     let config = $crateName::config::Builder::from(&shared_config)
                        ///         .retry_config(RetryConfig::disabled())
                        ///         .build();
                        ///     let client = $crateName::Client::from_conf(config);
                        /// ## }
                        """,
                        *codegenScope
                    )
                }
            }
            else -> emptySection
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -52,3 +52,4 @@ fun RuntimeConfig.awsRuntimeDependency(name: String, features: Set<String> = set

fun RuntimeConfig.awsHttp(): CargoDependency = awsRuntimeDependency("aws-http")
fun RuntimeConfig.awsTypes(): CargoDependency = awsRuntimeDependency("aws-types")
fun RuntimeConfig.awsConfig(): CargoDependency = awsRuntimeDependency("aws-config")
+1 −0
Original line number Diff line number Diff line
@@ -130,6 +130,7 @@ fun generateSmithyBuild(services: List<AwsService>): String {
                        "moduleVersion": "${getProperty("aws.sdk.version")}",
                        "moduleAuthors": ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Russell Cohen <rcoh@amazon.com>"],
                        "moduleDescription": "${service.moduleDescription}",
                        ${service.examplesUri(project)?.let { """"examples": "$it",""" } ?: ""}
                        "moduleRepository": "https://github.com/awslabs/aws-sdk-rust",
                        "license": "Apache-2.0"
                        ${service.extraConfig ?: ""}
+2 −6
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ fun Project.docsLandingPage(awsServices: List<AwsService>, outputDir: File) {
        writer.write("| Service | Package |")
        writer.write("| ------- | ------- |")
        awsServices.sortedBy { it.humanName }.forEach {
            val items = listOfNotNull(cratesIo(it), docsRs(it), examples(it, project)).joinToString(" ")
            val items = listOfNotNull(cratesIo(it), docsRs(it), examplesLink(it, project)).joinToString(" ")
            writer.write(
                "| ${it.humanName} | $items |"
            )
@@ -41,11 +41,7 @@ fun Project.docsLandingPage(awsServices: List<AwsService>, outputDir: File) {
/**
 * Generate a link to the examples for a given service
 */
private fun examples(service: AwsService, project: Project) = if (with(service) { project.examples() }) {
    "([examples](https://github.com/awslabs/aws-sdk-rust/tree/main/examples/${service.module}))"
} else {
    null
}
private fun examplesLink(service: AwsService, project: Project) = service.examplesUri(project)?.let { "([examples]($it))" }

/**
 * Generate a link to the docs
+9 −1
Original line number Diff line number Diff line
@@ -89,7 +89,15 @@ data class AwsService(
    val humanName: String
) {
    fun files(): List<File> = listOf(modelFile) + extraFiles
    fun Project.examples(): Boolean = projectDir.resolve("examples").resolve(module).exists()
    fun Project.examples(): File = projectDir.resolve("examples").resolve(module)
    /**
     * Generate a link to the examples for a given service
     */
    fun examplesUri(project: Project) = if (project.examples().exists()) {
        "https://github.com/awslabs/aws-sdk-rust/tree/main/examples/$module"
    } else {
        null
    }
}

fun AwsService.crate(): String = "aws-sdk-$module"
Loading