Unverified Commit 2480d81b authored by david-perez's avatar david-perez Committed by GitHub
Browse files

Import the `{input, output, error}` modules in the RustDocs service stub example (#2096)

Instead of importing the types within those modules. It's thus easier to
see what the types' roles are when using them in arbitrary business
logic.

Before:

```rust
use my_service::input::Foo;
use my_service::output::Bar;

async fn my_handler(input: Foo) -> Bar { todo!() }
```

With this commit:

```rust
use my_service::{input, output};

async fn my_handler(input: input::Foo) -> output::Bar { todo!() }
```
parent 2cc7c24b
Loading
Loading
Loading
Loading
+6 −23
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape
import software.amazon.smithy.rust.codegen.core.util.outputShape

/**
Generates a stub for use within documentation.
 * Generates a handler implementation stub for use within documentation.
 */
class DocHandlerGenerator(
    codegenContext: CodegenContext,
@@ -31,41 +31,25 @@ class DocHandlerGenerator(
) {
    private val model = codegenContext.model
    private val symbolProvider = codegenContext.symbolProvider
    private val crateName = codegenContext.moduleUseName()

    private val inputSymbol = symbolProvider.toSymbol(operation.inputShape(model))
    private val outputSymbol = symbolProvider.toSymbol(operation.outputShape(model))
    private val errorSymbol = operation.errorSymbol(model, symbolProvider, CodegenTarget.SERVER)

    /**
     * Returns the imports required for the function signature
     */
    fun docSignatureImports(): Writable = writable {
        if (operation.errors.isNotEmpty()) {
            rust("$commentToken use $crateName::${ErrorsModule.name}::${errorSymbol.name};")
        }
        rust(
            """
            $commentToken use $crateName::${InputsModule.name}::${inputSymbol.name};
            $commentToken use $crateName::${OutputsModule.name}::${outputSymbol.name};
            """.trimIndent(),
        )
    }

    /**
     * Returns the function signature for an operation handler implementation. Used in the documentation.
     */
    fun docSignature(): Writable {
        val outputT = if (operation.errors.isEmpty()) {
            outputSymbol.name
            "${OutputsModule.name}::${outputSymbol.name}"
        } else {
            "Result<${outputSymbol.name}, ${errorSymbol.name}>"
            "Result<${OutputsModule.name}::${outputSymbol.name}, ${ErrorsModule.name}::${errorSymbol.name}>"
        }

        return writable {
            rust(
                """
                $commentToken async fn $handlerName(input: ${inputSymbol.name}) -> $outputT {
                $commentToken async fn $handlerName(input: ${InputsModule.name}::${inputSymbol.name}) -> $outputT {
                $commentToken     todo!()
                $commentToken }
                """.trimIndent(),
@@ -74,13 +58,12 @@ class DocHandlerGenerator(
    }

    fun render(writer: RustWriter) {
        // This assumes that the `error` (if applicable) `input`, and `output` modules have been imported by the
        // caller and hence are in scope.
        writer.rustTemplate(
            """
            #{Docs:W}
            $commentToken
            #{Handler:W}
            """,
            "Docs" to docSignatureImports(),
            "Handler" to docSignature(),
        )
    }
+5 −1
Original line number Diff line number Diff line
@@ -45,6 +45,9 @@ class ServerOperationShapeGenerator(
            //! ```no_run
            //! use $crateName::operation_shape::$firstOperationName;
            //! use #{SmithyHttpServer}::operation::OperationShapeExt;
            //!
            #{HandlerImports:W}
            //!
            #{Handler:W}
            //!
            //! let operation = $firstOperationName::from_handler(handler)
@@ -62,7 +65,8 @@ class ServerOperationShapeGenerator(
            "SmithyHttpServer" to
                ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(),
            "Tower" to ServerCargoDependency.Tower.toType(),
            "Handler" to DocHandlerGenerator(codegenContext, operations[0], "handler", "//!")::render,
            "Handler" to DocHandlerGenerator(codegenContext, operations[0], "handler", commentToken = "//!")::render,
            "HandlerImports" to handlerImports(crateName, operations, commentToken = "//!"),
        )
        for (operation in operations) {
            ServerOperationGenerator(codegenContext, operation).render(writer)
+5 −3
Original line number Diff line number Diff line
@@ -56,8 +56,7 @@ open class ServerServiceGenerator(
                )
        val crateName = codegenContext.moduleUseName()
        val builderName = "${serviceName}Builder"
        val service = codegenContext.serviceShape
        val hasErrors = service.operations.any { codegenContext.model.expectShape(it).asOperationShape().get().errors.isNotEmpty() }
        val hasErrors = operations.any { it.errors.isNotEmpty() }
        val handlers: Writable = operations
            .map { operation ->
                DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!")::render
@@ -212,6 +211,8 @@ open class ServerServiceGenerator(
            //!    }
            //! }
            //!
            #{HandlerImports:W}
            //!
            #{Handlers:W}
            //!
            //! ```
@@ -223,6 +224,7 @@ open class ServerServiceGenerator(
            //! [hyper server]: https://docs.rs/hyper/latest/hyper/server/index.html
            //! [Service]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html
            """,
            "HandlerImports" to handlerImports(crateName, operations, commentToken = "//!"),
            "Handlers" to handlers,
            "ExampleHandler" to operations.take(1).map { operation -> DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() },
            "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(),
+21 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule
import software.amazon.smithy.rust.codegen.core.smithy.InputsModule
import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
@@ -109,6 +112,8 @@ class ServerServiceGeneratorV2(
                /// ```no_run
                /// use $crateName::$serviceName;
                ///
                #{HandlerImports:W}
                ///
                #{Handler:W}
                ///
                /// let app = $serviceName::builder_without_plugins()
@@ -157,6 +162,7 @@ class ServerServiceGeneratorV2(
                """,
                "Protocol" to protocol.markerStruct(),
                "Handler" to DocHandlerGenerator(codegenContext, operationShape, "handler", "///")::render,
                "HandlerImports" to handlerImports(crateName, operations),
                *codegenScope,
            )

@@ -490,3 +496,18 @@ class ServerServiceGeneratorV2(
        )
    }
}

/**
 * Returns a writable to import the necessary modules used by a handler implementation stub.
 *
 * ```rust
 * use my_service::{input, output, error};
 * ```
 */
fun handlerImports(crateName: String, operations: Collection<OperationShape>, commentToken: String = "///") = writable {
    val hasErrors = operations.any { it.errors.isNotEmpty() }
    val errorImport = if (hasErrors) ", ${ErrorsModule.name}" else ""
    if (operations.isNotEmpty()) {
        rust("$commentToken use $crateName::{${InputsModule.name}, ${OutputsModule.name}$errorImport};")
    }
}