diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 4cfcf7807a..b42acc11bb 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -83,4 +83,61 @@ author = "jdisanti" message = "MSRV increased from `1.54` to `1.56.1` per our 2-behind MSRV policy." references = ["smithy-rs#1130"] meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "jdisanti" \ No newline at end of file +author = "jdisanti" + +[[aws-sdk-rust]] +message = """ +Fluent clients for all services no longer have generics, and now use `DynConnector` and `DynMiddleware` to allow +for connector/middleware customization. This should only break references to the client that specified generic types for it. + +If you customized the AWS client's connector or middleware with something like the following: +```rust +let client = aws_sdk_s3::Client::with_config( + aws_sdk_s3::client::Builder::new() + .connector(my_custom_connector) // Connector customization + .middleware(my_custom_middleware) // Middleware customization + .default_async_sleep() + .build(), + config +); +``` +Then you will need to wrap the custom connector or middleware in +[`DynConnector`](https://docs.rs/aws-smithy-client/0.36.0/aws_smithy_client/erase/struct.DynConnector.html) +and +[`DynMiddleware`](https://docs.rs/aws-smithy-client/0.36.0/aws_smithy_client/erase/struct.DynMiddleware.html) +respectively: +```rust +let client = aws_sdk_s3::Client::with_config( + aws_sdk_s3::client::Builder::new() + .connector(DynConnector::new(my_custom_connector)) // Now with `DynConnector` + .middleware(DynMiddleware::new(my_custom_middleware)) // Now with `DynMiddleware` + .default_async_sleep() + .build(), + config +); +``` + +If you had functions that took a generic connector, such as the following: +```rust +fn some_function(conn: C) -> Result<()> +where + C: aws_smithy_client::bounds::SmithyConnector + Send + 'static, + E: Into +{ + // ... +} +``` + +Then the generics and trait bounds will no longer be necessary: +```rust +fn some_function(conn: DynConnector) -> Result<()> { + // ... +} +``` + +Similarly, functions that took a generic middleware can replace the generic with `DynMiddleware` and +remove their trait bounds. +""" +references = ["smithy-rs#1132"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index e6c593c7a5..3dc14dcb1b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rustsdk +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.TitleTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute @@ -23,17 +24,18 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig 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.smithy.generators.client.FluentClientCustomization +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientGenerics +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.util.expectTrait import software.amazon.smithy.rustsdk.AwsRuntimeType.defaultMiddleware private class Types(runtimeConfig: RuntimeConfig) { private val smithyClientDep = CargoDependency.SmithyClient(runtimeConfig) + private val smithyHttpDep = CargoDependency.SmithyHttp(runtimeConfig) val awsTypes = awsTypes(runtimeConfig).asType() val smithyClientRetry = RuntimeType("retry", smithyClientDep, "aws_smithy_client") @@ -41,6 +43,33 @@ private class Types(runtimeConfig: RuntimeConfig) { val defaultMiddleware = runtimeConfig.defaultMiddleware() val dynConnector = RuntimeType("DynConnector", smithyClientDep, "aws_smithy_client::erase") + val dynMiddleware = RuntimeType("DynMiddleware", smithyClientDep, "aws_smithy_client::erase") + val smithyConnector = RuntimeType("SmithyConnector", smithyClientDep, "aws_smithy_client::bounds") + + val connectorError = RuntimeType("ConnectorError", smithyHttpDep, "aws_smithy_http::result") +} + +private class AwsClientGenerics(private val types: Types) : FluentClientGenerics { + /** Declaration with defaults set */ + override val decl = writable { } + + /** Instantiation of the Smithy client generics */ + override val smithyInst = writable { + rustTemplate( + "<#{DynConnector}, #{DynMiddleware}<#{DynConnector}>>", + "DynConnector" to types.dynConnector, + "DynMiddleware" to types.dynMiddleware + ) + } + + /** Instantiation */ + override val inst = "" + + /** Trait bounds */ + override val bounds = writable { } + + /** Bounds for generated `send()` functions */ + override fun sendBounds(input: Symbol, output: Symbol, error: RuntimeType): Writable = writable { } } class AwsFluentClientDecorator : RustCodegenDecorator { @@ -53,12 +82,7 @@ class AwsFluentClientDecorator : RustCodegenDecorator { val types = Types(codegenContext.runtimeConfig) FluentClientGenerator( codegenContext, - generics = ClientGenerics( - connectorDefault = types.dynConnector, - middlewareDefault = types.defaultMiddleware, - retryDefault = types.smithyClientRetry.member("Standard"), - client = types.awsSmithyClient - ), + generics = AwsClientGenerics(types), customizations = listOf( AwsPresignedFluentBuilderMethod(codegenContext.runtimeConfig), AwsFluentClientDocs(codegenContext) @@ -88,33 +112,34 @@ class AwsFluentClientDecorator : RustCodegenDecorator { } } -private class AwsFluentClientExtensions(private val types: Types) { - val clientGenerics = ClientGenerics( - connectorDefault = types.dynConnector, - middlewareDefault = types.defaultMiddleware, - retryDefault = types.smithyClientRetry.member("Standard"), - client = types.awsSmithyClient - ) - +private class AwsFluentClientExtensions(types: Types) { private val codegenScope = arrayOf( "Middleware" to types.defaultMiddleware, "retry" to types.smithyClientRetry, "DynConnector" to types.dynConnector, - "aws_smithy_client" to types.awsSmithyClient + "DynMiddleware" to types.dynMiddleware, + "SmithyConnector" to types.smithyConnector, + "ConnectorError" to types.connectorError, + "aws_smithy_client" to types.awsSmithyClient, + "aws_types" to types.awsTypes, ) fun render(writer: RustWriter) { - writer.rustBlockTemplate("impl Client", *codegenScope) { + writer.rustBlockTemplate("impl Client", *codegenScope) { rustTemplate( """ /// Creates a client with the given service config and connector override. - pub fn from_conf_conn(conf: crate::Config, conn: C) -> Self { + pub fn from_conf_conn(conf: crate::Config, conn: C) -> Self + where + C: #{SmithyConnector} + Send + 'static, + E: Into<#{ConnectorError}>, + { let retry_config = conf.retry_config.as_ref().cloned().unwrap_or_default(); let timeout_config = conf.timeout_config.as_ref().cloned().unwrap_or_default(); let sleep_impl = conf.sleep_impl.clone(); let mut builder = #{aws_smithy_client}::Builder::new() - .connector(conn) - .middleware(#{Middleware}::new()); + .connector(#{DynConnector}::new(conn)) + .middleware(#{DynMiddleware}::new(#{Middleware}::new())); builder.set_retry_config(retry_config.into()); builder.set_timeout_config(timeout_config); if let Some(sleep_impl) = sleep_impl { @@ -123,13 +148,7 @@ private class AwsFluentClientExtensions(private val types: Types) { let client = builder.build(); Self { handle: std::sync::Arc::new(Handle { client, conf }) } } - """, - *codegenScope - ) - } - writer.rustBlockTemplate("impl Client<#{DynConnector}, #{Middleware}, #{retry}::Standard>", *codegenScope) { - rustTemplate( - """ + /// Creates a new client from a shared config. ##[cfg(any(feature = "rustls", feature = "native-tls"))] pub fn new(config: &#{aws_types}::config::Config) -> Self { @@ -143,7 +162,7 @@ private class AwsFluentClientExtensions(private val types: Types) { let timeout_config = conf.timeout_config.as_ref().cloned().unwrap_or_default(); let sleep_impl = conf.sleep_impl.clone(); let mut builder = #{aws_smithy_client}::Builder::dyn_https() - .middleware(#{Middleware}::new()); + .middleware(#{DynMiddleware}::new(#{Middleware}::new())); builder.set_retry_config(retry_config.into()); builder.set_timeout_config(timeout_config); // the builder maintains a try-state. To avoid suppressing the warning when sleep is unset, @@ -156,9 +175,7 @@ private class AwsFluentClientExtensions(private val types: Types) { Self { handle: std::sync::Arc::new(Handle { client, conf }) } } """, - "aws_smithy_client" to types.awsSmithyClient, - "aws_types" to types.awsTypes, - "Middleware" to types.defaultMiddleware + *codegenScope, ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 6340665218..5ab8e4c90d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -32,8 +32,8 @@ import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator -import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientCustomization -import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientSection +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientCustomization +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.smithy.protocols.HttpBoundProtocolBodyGenerator diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt index b98d013ebb..8b5db7a7ab 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customize/RustCodegenDecorator.kt @@ -13,9 +13,9 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientDecorator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.util.deepMergeWith diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt index 0fec1ef84a..916d3657be 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/PaginatorGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.rustlang.writable import software.amazon.smithy.rust.codegen.smithy.CodegenContext import software.amazon.smithy.rust.codegen.smithy.RuntimeType import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientGenerics 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.PANIC @@ -45,13 +46,13 @@ class PaginatorGenerator private constructor( private val symbolProvider: RustSymbolProvider, service: ServiceShape, operation: OperationShape, - private val generics: ClientGenerics + private val generics: FluentClientGenerics ) { companion object { fun paginatorType( codegenContext: CodegenContext, - generics: ClientGenerics, + generics: FluentClientGenerics, operationShape: OperationShape ): RuntimeType? { return if (operationShape.isPaginated(codegenContext.model)) { @@ -79,7 +80,9 @@ class PaginatorGenerator private constructor( documentation = "Paginators for the service" ) + private val inputType = symbolProvider.toSymbol(operation.inputShape(model)) private val outputType = operation.outputShape(model) + private val errorType = operation.errorSymbol(symbolProvider) private fun paginatorType(): RuntimeType = RuntimeType.forInlineFun( paginatorName, @@ -91,12 +94,13 @@ class PaginatorGenerator private constructor( "generics" to generics.decl, "bounds" to generics.bounds, "page_size_setter" to pageSizeSetter(), + "send_bounds" to generics.sendBounds(inputType, symbolProvider.toSymbol(outputType), errorType), // Operation Types "operation" to symbolProvider.toSymbol(operation), - "Input" to symbolProvider.toSymbol(operation.inputShape(model)), - "Output" to symbolProvider.toSymbol(operation.outputShape(model)), - "Error" to operation.errorSymbol(symbolProvider), + "Input" to inputType, + "Output" to symbolProvider.toSymbol(outputType), + "Error" to errorType, "Builder" to operation.inputShape(model).builderSymbol(symbolProvider), // SDK Types @@ -125,7 +129,7 @@ class PaginatorGenerator private constructor( builder: #{Builder} } - impl${generics.inst} ${paginatorName}${generics.inst} where #{bounds:W} { + impl${generics.inst} ${paginatorName}${generics.inst} #{bounds:W} { /// Create a new paginator-wrapper pub(crate) fn new(handle: std::sync::Arc, builder: #{Builder}) -> Self { Self { @@ -143,13 +147,7 @@ class PaginatorGenerator private constructor( /// /// _Note:_ No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next)). pub fn send(self) -> impl #{Stream}>> + Unpin - where - R::Policy: #{client}::bounds::SmithyRetryPolicy< - #{Input}OperationOutputAlias, - #{Output}, - #{Error}, - #{Input}OperationRetryAlias - >, { + #{send_bounds:W} { // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; @@ -246,20 +244,14 @@ class PaginatorGenerator private constructor( /// This is created with [`.items()`]($paginatorName::items) pub struct ${paginatorName}Items#{generics:W}($paginatorName${generics.inst}); - impl ${generics.inst} ${paginatorName}Items${generics.inst} where #{bounds:W} { + impl ${generics.inst} ${paginatorName}Items${generics.inst} #{bounds:W} { /// Create the pagination stream /// /// _Note: No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next))._ /// /// To read the entirety of the paginator, use [`.collect::, _>()`](tokio_stream::StreamExt::collect). pub fn send(self) -> impl #{Stream}>> + Unpin - where - R::Policy: #{client}::bounds::SmithyRetryPolicy< - #{Input}OperationOutputAlias, - #{Output}, - #{Error}, - #{Input}OperationRetryAlias - >, { + #{send_bounds:W} { #{fn_stream}::TryFlatMap::new(self.0.send()).flat_map(|page| #{extract_items}(page).unwrap_or_default().into_iter()) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientCore.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt similarity index 95% rename from codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientCore.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt index c9076a5284..1bcc74cc78 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientCore.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.rust.codegen.smithy.generators +package software.amazon.smithy.rust.codegen.smithy.generators.client import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.smithy.generators.setterName class FluentClientCore(private val model: Model) { /** Generate and write Rust code for a builder method that sets a Vec */ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt similarity index 92% rename from codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt index 908710b0e6..68f206a046 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/FluentClientDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -package software.amazon.smithy.rust.codegen.smithy.generators +package software.amazon.smithy.rust.codegen.smithy.generators.client import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model @@ -45,7 +45,13 @@ import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.customize.Section import software.amazon.smithy.rust.codegen.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection +import software.amazon.smithy.rust.codegen.smithy.generators.PaginatorGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.smithy.generators.error.errorSymbol +import software.amazon.smithy.rust.codegen.smithy.generators.isPaginated +import software.amazon.smithy.rust.codegen.smithy.generators.setterName import software.amazon.smithy.rust.codegen.smithy.rustType import software.amazon.smithy.rust.codegen.util.inputShape import software.amazon.smithy.rust.codegen.util.orNull @@ -104,42 +110,6 @@ sealed class FluentClientSection(name: String) : Section(name) { abstract class FluentClientCustomization : NamedSectionGenerator() -data class ClientGenerics( - val connectorDefault: RuntimeType?, - val middlewareDefault: RuntimeType?, - val retryDefault: RuntimeType?, - val client: RuntimeType -) { - /** Declaration with defaults set */ - val decl = writable { - rustTemplate( - "", - "c" to defaultType(connectorDefault), - "m" to defaultType(middlewareDefault), - "r" to defaultType(retryDefault) - ) - } - - /** Instantiation */ - val inst: String = "" - - /** Trait bounds */ - val bounds = writable { - rustTemplate( - """ - C: #{client}::bounds::SmithyConnector, - M: #{client}::bounds::SmithyMiddleware, - R: #{client}::retry::NewRequestPolicy, - """, - "client" to client - ) - } - - private fun defaultType(default: RuntimeType?) = writable { - default?.also { rust("= #T", default) } - } -} - class GenericFluentClient(codegenContext: CodegenContext) : FluentClientCustomization() { private val moduleUseName = codegenContext.moduleUseName() private val clientDep = CargoDependency.SmithyClient(codegenContext.runtimeConfig) @@ -273,7 +243,7 @@ class GenericFluentClient(codegenContext: CodegenContext) : FluentClientCustomiz class FluentClientGenerator( private val codegenContext: CodegenContext, - private val generics: ClientGenerics = ClientGenerics( + private val generics: FluentClientGenerics = FlexibleClientGenerics( connectorDefault = null, middlewareDefault = null, retryDefault = CargoDependency.SmithyClient(codegenContext.runtimeConfig).asType().member("retry::Standard"), @@ -312,7 +282,7 @@ class FluentClientGenerator( """ ##[derive(Debug)] pub(crate) struct Handle#{generics_decl:W} { - pub(crate) client: #{client}::Client${generics.inst}, + pub(crate) client: #{client}::Client#{smithy_inst:W}, pub(crate) conf: crate::Config, } @@ -331,15 +301,15 @@ class FluentClientGenerator( ##[doc(inline)] pub use #{client}::Builder; - impl${generics.inst} From<#{client}::Client${generics.inst}> for Client${generics.inst} { - fn from(client: #{client}::Client${generics.inst}) -> Self { + impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { + fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { Self::with_config(client, crate::Config::builder().build()) } } impl${generics.inst} Client${generics.inst} { /// Creates a client with the given service configuration. - pub fn with_config(client: #{client}::Client${generics.inst}, conf: crate::Config) -> Self { + pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { Self { handle: std::sync::Arc::new(Handle { client, @@ -355,6 +325,7 @@ class FluentClientGenerator( } """, "generics_decl" to generics.decl, + "smithy_inst" to generics.smithyInst, "client" to clientDep.asType(), "client_docs" to writable { @@ -368,7 +339,7 @@ class FluentClientGenerator( }, ) writer.rustBlockTemplate( - "impl${generics.inst} Client${generics.inst} where #{bounds:W}", + "impl${generics.inst} Client${generics.inst} #{bounds:W}", "client" to clientDep.asType(), "bounds" to generics.bounds ) { @@ -467,10 +438,13 @@ class FluentClientGenerator( ) rustBlockTemplate( - "impl${generics.inst} ${operationSymbol.name}${generics.inst} where #{bounds:W}", + "impl${generics.inst} ${operationSymbol.name}${generics.inst} #{bounds:W}", "client" to clientDep.asType(), "bounds" to generics.bounds ) { + val inputType = symbolProvider.toSymbol(operation.inputShape(model)) + val outputType = symbolProvider.toSymbol(operation.outputShape(model)) + val errorType = operation.errorSymbol(symbolProvider) rustTemplate( """ /// Creates a new `${operationSymbol.name}`. @@ -487,12 +461,7 @@ class FluentClientGenerator( /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. pub async fn send(self) -> std::result::Result<#{ok}, #{sdk_err}<#{operation_err}>> - where - R::Policy: #{client}::bounds::SmithyRetryPolicy<#{input}OperationOutputAlias, - #{ok}, - #{operation_err}, - #{input}OperationRetryAlias>, - { + #{send_bounds:W} { let op = self.inner.build().map_err(|err|#{sdk_err}::ConstructionFailure(err.into()))? .make_operation(&self.handle.conf) .await @@ -500,12 +469,11 @@ class FluentClientGenerator( self.handle.client.call(op).await } """, - "input" to symbolProvider.toSymbol(operation.inputShape(model)), - "ok" to symbolProvider.toSymbol(operation.outputShape(model)), - "operation_err" to operation.errorSymbol(symbolProvider), + "ok" to outputType, + "operation_err" to errorType, "sdk_err" to CargoDependency.SmithyHttp(runtimeConfig).asType() .copy(name = "result::SdkError"), - "client" to clientDep.asType(), + "send_bounds" to generics.sendBounds(inputType, outputType, errorType) ) PaginatorGenerator.paginatorType(codegenContext, generics, operation)?.also { paginatorType -> rustTemplate( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientGenerics.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientGenerics.kt new file mode 100644 index 0000000000..479d457e89 --- /dev/null +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientGenerics.kt @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +package software.amazon.smithy.rust.codegen.smithy.generators.client + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.rust +import software.amazon.smithy.rust.codegen.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.rustlang.writable +import software.amazon.smithy.rust.codegen.smithy.RuntimeType + +interface FluentClientGenerics { + /** Declaration with defaults set */ + val decl: Writable + + /** Instantiation of the Smithy client generics */ + val smithyInst: Writable + + /** Instantiation */ + val inst: String + + /** Bounds */ + val bounds: Writable + + /** Bounds for generated `send()` functions */ + fun sendBounds(input: Symbol, output: Symbol, error: RuntimeType): Writable +} + +data class FlexibleClientGenerics( + val connectorDefault: RuntimeType?, + val middlewareDefault: RuntimeType?, + val retryDefault: RuntimeType?, + val client: RuntimeType +) : FluentClientGenerics { + /** Declaration with defaults set */ + override val decl = writable { + rustTemplate( + "", + "c" to defaultType(connectorDefault), + "m" to defaultType(middlewareDefault), + "r" to defaultType(retryDefault) + ) + } + + /** Instantiation of the Smithy client generics */ + override val smithyInst = writable { rust("") } + + /** Instantiation */ + override val inst: String = "" + + /** Trait bounds */ + override val bounds = writable { + rustTemplate( + """ + where + C: #{client}::bounds::SmithyConnector, + M: #{client}::bounds::SmithyMiddleware, + R: #{client}::retry::NewRequestPolicy, + """, + "client" to client + ) + } + + /** Bounds for generated `send()` functions */ + override fun sendBounds(input: Symbol, output: Symbol, error: RuntimeType): Writable = writable { + rustTemplate( + """ + where + R::Policy: #{client}::bounds::SmithyRetryPolicy< + #{Input}OperationOutputAlias, + #{Output}, + #{Error}, + #{Input}OperationRetryAlias + > + """, + "client" to client, + "Input" to input, + "Output" to output, + "Error" to error, + ) + } + + private fun defaultType(default: RuntimeType?) = writable { + default?.also { rust("= #T", default) } + } +} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolGenerator.kt index 011d1edf0e..873a911fff 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/protocol/ProtocolGenerator.kt @@ -22,7 +22,7 @@ import software.amazon.smithy.rust.codegen.smithy.customize.OperationCustomizati import software.amazon.smithy.rust.codegen.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.smithy.generators.FluentClientGenerator +import software.amazon.smithy.rust.codegen.smithy.generators.client.FluentClientGenerator import software.amazon.smithy.rust.codegen.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.smithy.protocols.HttpLocation