Loading codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.Errors import software.amazon.smithy.rust.codegen.core.smithy.Inputs import software.amazon.smithy.rust.codegen.core.smithy.Outputs import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** Generates a stub for use within documentation. */ class DocHandlerGenerator(private val operation: OperationShape, private val commentToken: String = "//", private val codegenContext: CodegenContext) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val crateName = codegenContext.settings.moduleName.toSnakeCase() /** * Returns the function signature for an operation handler implementation. Used in the documentation. */ private fun OperationShape.docSignature(): Writable { val inputSymbol = symbolProvider.toSymbol(inputShape(model)) val outputSymbol = symbolProvider.toSymbol(outputShape(model)) val errorSymbol = errorSymbol(model, symbolProvider, CodegenTarget.SERVER) val outputT = if (errors.isEmpty()) { outputSymbol.name } else { "Result<${outputSymbol.name}, ${errorSymbol.name}>" } return writable { if (!errors.isEmpty()) { rust("$commentToken ## use $crateName::${Errors.namespace}::${errorSymbol.name};") } rust( """ $commentToken ## use $crateName::${Inputs.namespace}::${inputSymbol.name}; $commentToken ## use $crateName::${Outputs.namespace}::${outputSymbol.name}; $commentToken async fn handler(input: ${inputSymbol.name}) -> $outputT { $commentToken todo!() $commentToken } """.trimIndent(), ) } } fun render(writer: RustWriter) { operation.docSignature()(writer) } } codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationShapeGenerator.kt 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency class ServerOperationShapeGenerator( private val operations: List<OperationShape>, private val codegenContext: CodegenContext, ) { fun render(writer: RustWriter) { if (operations.isEmpty()) { return } val firstOperation = codegenContext.symbolProvider.toSymbol(operations[0]) val firstOperationName = firstOperation.name.toPascalCase() val crateName = codegenContext.settings.moduleName.toSnakeCase() writer.rustTemplate( """ //! A collection of zero-sized types (ZSTs) representing each operation defined in the service closure. //! //! ## Constructing an [`Operation`](#{SmithyHttpServer}::operation::OperationShapeExt) //! //! To apply middleware to specific operations the [`Operation`](#{SmithyHttpServer}::operation::Operation) //! API must be used. //! //! Using the [`OperationShapeExt`](#{SmithyHttpServer}::operation::OperationShapeExt) trait //! implemented on each ZST we can construct an [`Operation`](#{SmithyHttpServer}::operation::Operation) //! with appropriate constraints given by Smithy. //! //! #### Example //! //! ```no_run //! use $crateName::operation_shape::$firstOperationName; //! use #{SmithyHttpServer}::operation::OperationShapeExt; #{Handler:W} //! //! let operation = $firstOperationName::from_handler(handler) //! .layer(todo!("Provide a layer implementation")); //! ``` //! //! ## Use as Marker Structs //! //! The [plugin system](#{SmithyHttpServer}::plugin) also makes use of these ZSTs to parameterize //! [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. The traits, such as //! [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape) can be used to provide //! operation specific information to the [`Layer`](#{Tower}::Layer) being applied. """.trimIndent(), "SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(codegenContext.runtimeConfig).toType(), "Tower" to ServerCargoDependency.Tower.toType(), "Handler" to DocHandlerGenerator(operations[0], "//!", codegenContext)::render, ) for (operation in operations) { ServerOperationGenerator(codegenContext, operation).render(writer) } } } codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +2 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ open class ServerServiceGenerator( ) { private val index = TopDownIndex.of(codegenContext.model) protected val operations = index.getContainedOperations(codegenContext.serviceShape).sortedBy { it.id } private val serviceName = codegenContext.serviceShape.id.name.toString() /** * Render Service Specific code. Code will end up in different files via [useShapeWriter]. See `SymbolVisitor.kt` Loading @@ -42,7 +43,6 @@ open class ServerServiceGenerator( */ fun render() { rustCrate.lib { val serviceName = codegenContext.serviceShape.id.name.toString() rust("##[doc(inline, hidden)]") rust("pub use crate::service::$serviceName;") } Loading Loading @@ -85,9 +85,7 @@ open class ServerServiceGenerator( ), ), ) { for (operation in operations) { ServerOperationGenerator(codegenContext, operation).render(this) } ServerOperationShapeGenerator(operations, codegenContext).render(this) } // TODO(https://github.com/awslabs/smithy-rs/issues/1707): Remove, this is temporary. Loading codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +26 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol class ServerServiceGeneratorV2( codegenContext: CodegenContext, private val codegenContext: CodegenContext, private val protocol: ServerProtocol, ) { private val runtimeConfig = codegenContext.runtimeConfig Loading @@ -32,12 +32,14 @@ class ServerServiceGeneratorV2( arrayOf( "Bytes" to CargoDependency.Bytes.toType(), "Http" to CargoDependency.Http.toType(), "SmithyHttp" to CargoDependency.smithyHttp(runtimeConfig).toType(), "HttpBody" to CargoDependency.HttpBody.toType(), "SmithyHttpServer" to smithyHttpServer, "Tower" to CargoDependency.Tower.toType(), ) private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider val crateName = codegenContext.settings.moduleName.toSnakeCase() private val service = codegenContext.serviceShape private val serviceName = service.id.name.toPascalCase() Loading Loading @@ -101,6 +103,22 @@ class ServerServiceGeneratorV2( /// /// This should be an async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. /// /// ## Example /// /// ```no_run /// use $crateName::$serviceName; /// #{Handler:W} /// /// let app = $serviceName::builder_without_plugins() /// .$fieldName(handler) /// /* Set other handlers */ /// .build() /// .unwrap(); /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; /// ``` /// pub fn $fieldName<HandlerType, Extensions>(self, handler: HandlerType) -> Self where HandlerType: #{SmithyHttpServer}::operation::Handler<crate::operation_shape::$structName, Extensions>, Loading Loading @@ -138,6 +156,7 @@ class ServerServiceGeneratorV2( } """, "Protocol" to protocol.markerStruct(), "Handler" to DocHandlerGenerator(operationShape, "///", codegenContext)::render, *codegenScope, ) Loading Loading @@ -179,6 +198,9 @@ class ServerServiceGeneratorV2( /// Constructs a [`$serviceName`] from the arguments provided to the builder. /// /// Forgetting to register a handler for one or more operations will result in an error. /// /// Check out [`$builderName::build_unchecked`] if you'd prefer the service to return status code 500 when an /// unspecified route requested. pub fn build(self) -> Result<$serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>>, MissingOperationsError> { let router = { Loading Loading @@ -343,7 +365,7 @@ class ServerServiceGeneratorV2( /// Constructs a builder for [`$serviceName`]. /// /// Use [`$serviceName::builder_without_plugins`] if you need to specify plugins. /// Use [`$serviceName::builder_with_plugins`] if you need to specify plugins. pub fn builder_without_plugins<Body>() -> $builderName<Body, #{SmithyHttpServer}::plugin::IdentityPlugin> { Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin) } Loading Loading @@ -415,6 +437,8 @@ class ServerServiceGeneratorV2( private fun missingOperationsError(): Writable = writable { rust( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, Loading Loading
codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.Errors import software.amazon.smithy.rust.codegen.core.smithy.Inputs import software.amazon.smithy.rust.codegen.core.smithy.Outputs import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** Generates a stub for use within documentation. */ class DocHandlerGenerator(private val operation: OperationShape, private val commentToken: String = "//", private val codegenContext: CodegenContext) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider private val crateName = codegenContext.settings.moduleName.toSnakeCase() /** * Returns the function signature for an operation handler implementation. Used in the documentation. */ private fun OperationShape.docSignature(): Writable { val inputSymbol = symbolProvider.toSymbol(inputShape(model)) val outputSymbol = symbolProvider.toSymbol(outputShape(model)) val errorSymbol = errorSymbol(model, symbolProvider, CodegenTarget.SERVER) val outputT = if (errors.isEmpty()) { outputSymbol.name } else { "Result<${outputSymbol.name}, ${errorSymbol.name}>" } return writable { if (!errors.isEmpty()) { rust("$commentToken ## use $crateName::${Errors.namespace}::${errorSymbol.name};") } rust( """ $commentToken ## use $crateName::${Inputs.namespace}::${inputSymbol.name}; $commentToken ## use $crateName::${Outputs.namespace}::${outputSymbol.name}; $commentToken async fn handler(input: ${inputSymbol.name}) -> $outputT { $commentToken todo!() $commentToken } """.trimIndent(), ) } } fun render(writer: RustWriter) { operation.docSignature()(writer) } }
codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationShapeGenerator.kt 0 → 100644 +71 −0 Original line number Diff line number Diff line /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency class ServerOperationShapeGenerator( private val operations: List<OperationShape>, private val codegenContext: CodegenContext, ) { fun render(writer: RustWriter) { if (operations.isEmpty()) { return } val firstOperation = codegenContext.symbolProvider.toSymbol(operations[0]) val firstOperationName = firstOperation.name.toPascalCase() val crateName = codegenContext.settings.moduleName.toSnakeCase() writer.rustTemplate( """ //! A collection of zero-sized types (ZSTs) representing each operation defined in the service closure. //! //! ## Constructing an [`Operation`](#{SmithyHttpServer}::operation::OperationShapeExt) //! //! To apply middleware to specific operations the [`Operation`](#{SmithyHttpServer}::operation::Operation) //! API must be used. //! //! Using the [`OperationShapeExt`](#{SmithyHttpServer}::operation::OperationShapeExt) trait //! implemented on each ZST we can construct an [`Operation`](#{SmithyHttpServer}::operation::Operation) //! with appropriate constraints given by Smithy. //! //! #### Example //! //! ```no_run //! use $crateName::operation_shape::$firstOperationName; //! use #{SmithyHttpServer}::operation::OperationShapeExt; #{Handler:W} //! //! let operation = $firstOperationName::from_handler(handler) //! .layer(todo!("Provide a layer implementation")); //! ``` //! //! ## Use as Marker Structs //! //! The [plugin system](#{SmithyHttpServer}::plugin) also makes use of these ZSTs to parameterize //! [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. The traits, such as //! [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape) can be used to provide //! operation specific information to the [`Layer`](#{Tower}::Layer) being applied. """.trimIndent(), "SmithyHttpServer" to ServerCargoDependency.SmithyHttpServer(codegenContext.runtimeConfig).toType(), "Tower" to ServerCargoDependency.Tower.toType(), "Handler" to DocHandlerGenerator(operations[0], "//!", codegenContext)::render, ) for (operation in operations) { ServerOperationGenerator(codegenContext, operation).render(writer) } } }
codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +2 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ open class ServerServiceGenerator( ) { private val index = TopDownIndex.of(codegenContext.model) protected val operations = index.getContainedOperations(codegenContext.serviceShape).sortedBy { it.id } private val serviceName = codegenContext.serviceShape.id.name.toString() /** * Render Service Specific code. Code will end up in different files via [useShapeWriter]. See `SymbolVisitor.kt` Loading @@ -42,7 +43,6 @@ open class ServerServiceGenerator( */ fun render() { rustCrate.lib { val serviceName = codegenContext.serviceShape.id.name.toString() rust("##[doc(inline, hidden)]") rust("pub use crate::service::$serviceName;") } Loading Loading @@ -85,9 +85,7 @@ open class ServerServiceGenerator( ), ), ) { for (operation in operations) { ServerOperationGenerator(codegenContext, operation).render(this) } ServerOperationShapeGenerator(operations, codegenContext).render(this) } // TODO(https://github.com/awslabs/smithy-rs/issues/1707): Remove, this is temporary. Loading
codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGeneratorV2.kt +26 −2 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol class ServerServiceGeneratorV2( codegenContext: CodegenContext, private val codegenContext: CodegenContext, private val protocol: ServerProtocol, ) { private val runtimeConfig = codegenContext.runtimeConfig Loading @@ -32,12 +32,14 @@ class ServerServiceGeneratorV2( arrayOf( "Bytes" to CargoDependency.Bytes.toType(), "Http" to CargoDependency.Http.toType(), "SmithyHttp" to CargoDependency.smithyHttp(runtimeConfig).toType(), "HttpBody" to CargoDependency.HttpBody.toType(), "SmithyHttpServer" to smithyHttpServer, "Tower" to CargoDependency.Tower.toType(), ) private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider val crateName = codegenContext.settings.moduleName.toSnakeCase() private val service = codegenContext.serviceShape private val serviceName = service.id.name.toPascalCase() Loading Loading @@ -101,6 +103,22 @@ class ServerServiceGeneratorV2( /// /// This should be an async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. /// /// ## Example /// /// ```no_run /// use $crateName::$serviceName; /// #{Handler:W} /// /// let app = $serviceName::builder_without_plugins() /// .$fieldName(handler) /// /* Set other handlers */ /// .build() /// .unwrap(); /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; /// ``` /// pub fn $fieldName<HandlerType, Extensions>(self, handler: HandlerType) -> Self where HandlerType: #{SmithyHttpServer}::operation::Handler<crate::operation_shape::$structName, Extensions>, Loading Loading @@ -138,6 +156,7 @@ class ServerServiceGeneratorV2( } """, "Protocol" to protocol.markerStruct(), "Handler" to DocHandlerGenerator(operationShape, "///", codegenContext)::render, *codegenScope, ) Loading Loading @@ -179,6 +198,9 @@ class ServerServiceGeneratorV2( /// Constructs a [`$serviceName`] from the arguments provided to the builder. /// /// Forgetting to register a handler for one or more operations will result in an error. /// /// Check out [`$builderName::build_unchecked`] if you'd prefer the service to return status code 500 when an /// unspecified route requested. pub fn build(self) -> Result<$serviceName<#{SmithyHttpServer}::routing::Route<$builderBodyGenericTypeName>>, MissingOperationsError> { let router = { Loading Loading @@ -343,7 +365,7 @@ class ServerServiceGeneratorV2( /// Constructs a builder for [`$serviceName`]. /// /// Use [`$serviceName::builder_without_plugins`] if you need to specify plugins. /// Use [`$serviceName::builder_with_plugins`] if you need to specify plugins. pub fn builder_without_plugins<Body>() -> $builderName<Body, #{SmithyHttpServer}::plugin::IdentityPlugin> { Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin) } Loading Loading @@ -415,6 +437,8 @@ class ServerServiceGeneratorV2( private fun missingOperationsError(): Writable = writable { rust( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, Loading