Make service config just contain a `FrozenLayer` in the orchestrator mode (#2762)

## Motivation and Context
Service config structs now only contain a
`aws_smithy_types::config_bag::FrozenLayer` in the orchestrator mode (no
changes for the middleware mode).

## Description
This PR reduces the individual fields of service configs to contain just
`FrozenLayer`. This makes service configs work more seamlessly with
runtime plugins in the orchestrator mode.

Note that service config _builder_ s still contain individual fields.
We're planning to make them just contain
`aws_smithy_types::config_bag::Layer`. To do that, though, we need to
make `Layer` cloneable and that will be handled in a separate PR. For
builders, the only change you will in the PR is that their `build`
method will put fields into a `Layer`, freeze it, and pass it to service
configs.

This PR is marked as a breaking change because it's based on [another
PR](https://github.com/awslabs/smithy-rs/pull/2728) that's also breaking
change.

## Testing
- [x] Passed tests in CI

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Co-authored-by: Yuki Saito <awsaito@amazon.com>
Co-authored-by: Zelda Hessler <zhessler@amazon.com>
This commit is contained in:
ysaito1001 2023-06-15 18:46:24 -05:00 committed by GitHub
parent b2bdcba57a
commit 2e472d068e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 1208 additions and 458 deletions

View File

@ -1,3 +1,6 @@
allowed_external_types = [ allowed_external_types = [
"aws_smithy_async::rt::sleep::SharedAsyncSleep", "aws_smithy_async::rt::sleep::SharedAsyncSleep",
"aws_smithy_types::config_bag::storable::Storable",
"aws_smithy_types::config_bag::storable::StoreReplace",
"aws_smithy_types::config_bag::storable::Storer",
] ]

View File

@ -14,6 +14,7 @@ pub use lazy_caching::Builder as LazyBuilder;
use no_caching::NoCredentialsCache; use no_caching::NoCredentialsCache;
use crate::provider::{future, SharedCredentialsProvider}; use crate::provider::{future, SharedCredentialsProvider};
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::sync::Arc; use std::sync::Arc;
/// Asynchronous Cached Credentials Provider /// Asynchronous Cached Credentials Provider
@ -62,6 +63,10 @@ impl ProvideCachedCredentials for SharedCredentialsCache {
} }
} }
impl Storable for SharedCredentialsCache {
type Storer = StoreReplace<SharedCredentialsCache>;
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum Inner { pub(crate) enum Inner {
Lazy(lazy_caching::Builder), Lazy(lazy_caching::Builder),
@ -122,3 +127,7 @@ impl CredentialsCache {
} }
} }
} }
impl Storable for CredentialsCache {
type Storer = StoreReplace<CredentialsCache>;
}

View File

@ -72,6 +72,7 @@ construct credentials from hardcoded values.
//! ``` //! ```
use crate::Credentials; use crate::Credentials;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::sync::Arc; use std::sync::Arc;
/// Credentials provider errors /// Credentials provider errors
@ -350,3 +351,7 @@ impl ProvideCredentials for SharedCredentialsProvider {
self.0.provide_credentials() self.0.provide_credentials()
} }
} }
impl Storable for SharedCredentialsProvider {
type Storer = StoreReplace<SharedCredentialsProvider>;
}

View File

@ -2,13 +2,16 @@ allowed_external_types = [
"aws_credential_types::cache::CredentialsCache", "aws_credential_types::cache::CredentialsCache",
"aws_credential_types::provider::SharedCredentialsProvider", "aws_credential_types::provider::SharedCredentialsProvider",
"aws_smithy_async::rt::sleep::SharedAsyncSleep", "aws_smithy_async::rt::sleep::SharedAsyncSleep",
"aws_smithy_async::time::TimeSource",
"aws_smithy_async::time::SharedTimeSource", "aws_smithy_async::time::SharedTimeSource",
"aws_smithy_async::time::TimeSource",
"aws_smithy_client::http_connector", "aws_smithy_client::http_connector",
"aws_smithy_client::http_connector::HttpConnector", "aws_smithy_client::http_connector::HttpConnector",
"aws_smithy_http::endpoint::Endpoint", "aws_smithy_http::endpoint::Endpoint",
"aws_smithy_http::endpoint::EndpointPrefix", "aws_smithy_http::endpoint::EndpointPrefix",
"aws_smithy_http::endpoint::error::InvalidEndpointError", "aws_smithy_http::endpoint::error::InvalidEndpointError",
"aws_smithy_types::config_bag::storable::Storable",
"aws_smithy_types::config_bag::storable::StoreReplace",
"aws_smithy_types::config_bag::storable::Storer",
"aws_smithy_types::retry::RetryConfig", "aws_smithy_types::retry::RetryConfig",
"aws_smithy_types::timeout::TimeoutConfig", "aws_smithy_types::timeout::TimeoutConfig",
"http::uri::Uri", "http::uri::Uri",

View File

@ -5,6 +5,7 @@
//! New-type for a configurable app name. //! New-type for a configurable app name.
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::borrow::Cow; use std::borrow::Cow;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
@ -38,6 +39,10 @@ impl fmt::Display for AppName {
} }
} }
impl Storable for AppName {
type Storer = StoreReplace<AppName>;
}
impl AppName { impl AppName {
/// Creates a new app name. /// Creates a new app name.
/// ///

View File

@ -0,0 +1,31 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
//! Newtypes for endpoint-related parameters
//!
//! Parameters require newtypes so they have distinct types when stored in layers in config bag.
use aws_smithy_types::config_bag::{Storable, StoreReplace};
/// Newtype for `use_fips`
#[derive(Clone, Debug)]
pub struct UseFips(pub bool);
impl Storable for UseFips {
type Storer = StoreReplace<UseFips>;
}
/// Newtype for `use_dual_stack`
#[derive(Clone, Debug)]
pub struct UseDualStack(pub bool);
impl Storable for UseDualStack {
type Storer = StoreReplace<UseDualStack>;
}
/// Newtype for `endpoint_url`
#[derive(Clone, Debug)]
pub struct EndpointUrl(pub String);
impl Storable for EndpointUrl {
type Storer = StoreReplace<EndpointUrl>;
}

View File

@ -16,6 +16,7 @@
pub mod app_name; pub mod app_name;
pub mod build_metadata; pub mod build_metadata;
pub mod endpoint_config;
#[doc(hidden)] #[doc(hidden)]
pub mod os_shim_internal; pub mod os_shim_internal;
pub mod region; pub mod region;

View File

@ -5,6 +5,7 @@
//! Region type for determining the endpoint to send requests to. //! Region type for determining the endpoint to send requests to.
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
@ -35,6 +36,10 @@ impl Display for Region {
} }
} }
impl Storable for Region {
type Storer = StoreReplace<Region>;
}
impl Region { impl Region {
/// Creates a new `Region` from the given string. /// Creates a new `Region` from the given string.
pub fn new(region: impl Into<Cow<'static, str>>) -> Self { pub fn new(region: impl Into<Cow<'static, str>>) -> Self {

View File

@ -27,7 +27,7 @@ class CredentialsCacheDecorator : ClientCodegenDecorator {
codegenContext: ClientCodegenContext, codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>, baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> { ): List<ConfigCustomization> {
return baseCustomizations + CredentialCacheConfig(codegenContext.runtimeConfig) return baseCustomizations + CredentialCacheConfig(codegenContext)
} }
override fun operationCustomizations( override fun operationCustomizations(
@ -49,44 +49,65 @@ class CredentialsCacheDecorator : ClientCodegenDecorator {
/** /**
* Add a `.credentials_cache` field and builder to the `Config` for a given service * Add a `.credentials_cache` field and builder to the `Config` for a given service
*/ */
class CredentialCacheConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val codegenScope = arrayOf( private val codegenScope = arrayOf(
"cache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache"), "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::CredentialsCache"),
"provider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider"),
"DefaultProvider" to defaultProvider(), "DefaultProvider" to defaultProvider(),
"SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::SharedCredentialsCache"),
"SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"),
) )
override fun section(section: ServiceConfig) = writable { override fun section(section: ServiceConfig) = writable {
when (section) { when (section) {
ServiceConfig.ConfigStruct -> rustTemplate( ServiceConfig.ConfigStruct -> {
"""pub(crate) credentials_cache: #{cache}::SharedCredentialsCache,""", if (runtimeMode.defaultToMiddleware) {
*codegenScope, rustTemplate(
) """pub(crate) credentials_cache: #{SharedCredentialsCache},""",
*codegenScope,
ServiceConfig.ConfigImpl -> rustTemplate( )
"""
/// Returns the credentials cache.
pub fn credentials_cache(&self) -> #{cache}::SharedCredentialsCache {
self.credentials_cache.clone()
} }
""", }
*codegenScope,
) ServiceConfig.ConfigImpl -> {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
/// Returns the credentials cache.
pub fn credentials_cache(&self) -> #{SharedCredentialsCache} {
self.inner.load::<#{SharedCredentialsCache}>().expect("credentials cache should be set").clone()
}
""",
*codegenScope,
)
} else {
rustTemplate(
"""
/// Returns the credentials cache.
pub fn credentials_cache(&self) -> #{SharedCredentialsCache} {
self.credentials_cache.clone()
}
""",
*codegenScope,
)
}
}
ServiceConfig.BuilderStruct -> ServiceConfig.BuilderStruct ->
rustTemplate("credentials_cache: Option<#{cache}::CredentialsCache>,", *codegenScope) rustTemplate("credentials_cache: Option<#{CredentialsCache}>,", *codegenScope)
ServiceConfig.BuilderImpl -> { ServiceConfig.BuilderImpl -> {
rustTemplate( rustTemplate(
""" """
/// Sets the credentials cache for this service /// Sets the credentials cache for this service
pub fn credentials_cache(mut self, credentials_cache: #{cache}::CredentialsCache) -> Self { pub fn credentials_cache(mut self, credentials_cache: #{CredentialsCache}) -> Self {
self.set_credentials_cache(Some(credentials_cache)); self.set_credentials_cache(Some(credentials_cache));
self self
} }
/// Sets the credentials cache for this service /// Sets the credentials cache for this service
pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{cache}::CredentialsCache>) -> &mut Self { pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{CredentialsCache}>) -> &mut Self {
self.credentials_cache = credentials_cache; self.credentials_cache = credentials_cache;
self self
} }
@ -95,29 +116,56 @@ class CredentialCacheConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization(
) )
} }
ServiceConfig.BuilderBuild -> rustTemplate( ServiceConfig.BuilderBuild -> {
""" if (runtimeMode.defaultToOrchestrator) {
credentials_cache: self rustTemplate(
.credentials_cache """
.unwrap_or_else({ layer.store_put(
let sleep = self.sleep_impl.clone(); self.credentials_cache
|| match sleep { .unwrap_or_else({
Some(sleep) => { let sleep = self.sleep_impl.clone();
#{cache}::CredentialsCache::lazy_builder() || match sleep {
.sleep(sleep) Some(sleep) => {
.into_credentials_cache() #{CredentialsCache}::lazy_builder()
} .sleep(sleep)
None => #{cache}::CredentialsCache::lazy(), .into_credentials_cache()
} }
}) None => #{CredentialsCache}::lazy(),
.create_cache( }
self.credentials_provider.unwrap_or_else(|| { })
#{provider}::SharedCredentialsProvider::new(#{DefaultProvider}) .create_cache(self.credentials_provider.unwrap_or_else(|| {
}) #{SharedCredentialsProvider}::new(#{DefaultProvider})
), })),
""", );
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
credentials_cache: self
.credentials_cache
.unwrap_or_else({
let sleep = self.sleep_impl.clone();
|| match sleep {
Some(sleep) => {
#{CredentialsCache}::lazy_builder()
.sleep(sleep)
.into_credentials_cache()
}
None => #{CredentialsCache}::lazy(),
}
})
.create_cache(
self.credentials_provider.unwrap_or_else(|| {
#{SharedCredentialsProvider}::new(#{DefaultProvider})
})
),
""",
*codegenScope,
)
}
}
else -> emptySection else -> emptySection
} }

View File

@ -22,18 +22,21 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointRulese
import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam
import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.docs
import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.PANIC
import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.dq
import software.amazon.smithy.rust.codegen.core.util.extendIf import software.amazon.smithy.rust.codegen.core.util.extendIf
import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.orNull
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
import java.util.Optional import java.util.Optional
/** load a builtIn parameter from a ruleset by name */ /** load a builtIn parameter from a ruleset by name */
@ -48,14 +51,27 @@ fun ClientCodegenContext.getBuiltIn(builtIn: String): Parameter? {
return rules.getBuiltIn(builtIn) return rules.getBuiltIn(builtIn)
} }
private fun toConfigParam(parameter: Parameter): ConfigParam = ConfigParam( private fun promotedBuiltins(parameter: Parameter) =
parameter.name.rustName(), parameter == Builtins.FIPS || parameter == Builtins.DUALSTACK || parameter == Builtins.SDK_ENDPOINT
when (parameter.type!!) {
ParameterType.STRING -> RuntimeType.String.toSymbol() private fun ConfigParam.Builder.toConfigParam(parameter: Parameter, runtimeConfig: RuntimeConfig): ConfigParam =
ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol() this.name(this.name ?: parameter.name.rustName())
}, .type(
parameter.documentation.orNull()?.let { writable { docs(it) } }, when (parameter.type!!) {
) ParameterType.STRING -> RuntimeType.String.toSymbol()
ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol()
},
)
.newtype(
when (promotedBuiltins(parameter)) {
true -> AwsRuntimeType.awsTypes(runtimeConfig)
.resolve("endpoint_config::${this.name!!.toPascalCase()}")
false -> configParamNewtype(this.name!!.toPascalCase(), this.type!!, runtimeConfig)
},
)
.setterDocs(this.setterDocs ?: parameter.documentation.orNull()?.let { writable { docs(it) } })
.build()
fun Model.loadBuiltIn(serviceId: ShapeId, builtInSrc: Parameter): Parameter? { fun Model.loadBuiltIn(serviceId: ShapeId, builtInSrc: Parameter): Parameter? {
val model = this val model = this
@ -82,14 +98,14 @@ fun Model.sdkConfigSetter(
} }
/** /**
* Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide [clientParam] * Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide
* which allows control over the config parameter that will be generated. * [clientParam.Builder] which allows control over the config parameter that will be generated.
*/ */
fun decoratorForBuiltIn( fun decoratorForBuiltIn(
builtIn: Parameter, builtIn: Parameter,
clientParam: ConfigParam? = null, clientParamBuilder: ConfigParam.Builder? = null,
): ClientCodegenDecorator { ): ClientCodegenDecorator {
val nameOverride = clientParam?.name val nameOverride = clientParamBuilder?.name
val name = nameOverride ?: builtIn.name.rustName() val name = nameOverride ?: builtIn.name.rustName()
return object : ClientCodegenDecorator { return object : ClientCodegenDecorator {
override val name: String = "Auto${builtIn.builtIn.get()}" override val name: String = "Auto${builtIn.builtIn.get()}"
@ -100,7 +116,7 @@ fun decoratorForBuiltIn(
override fun extraSections(codegenContext: ClientCodegenContext): List<AdHocCustomization> { override fun extraSections(codegenContext: ClientCodegenContext): List<AdHocCustomization> {
return listOfNotNull( return listOfNotNull(
codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParam?.name), codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParamBuilder?.name),
) )
} }
@ -110,7 +126,9 @@ fun decoratorForBuiltIn(
): List<ConfigCustomization> { ): List<ConfigCustomization> {
return baseCustomizations.extendIf(rulesetContainsBuiltIn(codegenContext)) { return baseCustomizations.extendIf(rulesetContainsBuiltIn(codegenContext)) {
standardConfigParam( standardConfigParam(
clientParam ?: toConfigParam(builtIn), clientParamBuilder?.toConfigParam(builtIn, codegenContext.runtimeConfig) ?: ConfigParam.Builder()
.toConfigParam(builtIn, codegenContext.runtimeConfig),
codegenContext,
) )
} }
} }
@ -120,11 +138,16 @@ fun decoratorForBuiltIn(
override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? = override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? =
when (parameter.builtIn) { when (parameter.builtIn) {
builtIn.builtIn -> writable { builtIn.builtIn -> writable {
rust("$configRef.$name") if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) {
rust("$configRef.$name()")
} else {
rust("$configRef.$name")
}
if (parameter.type == ParameterType.STRING) { if (parameter.type == ParameterType.STRING) {
rust(".clone()") rust(".clone()")
} }
} }
else -> null else -> null
} }
@ -173,6 +196,6 @@ val PromotedBuiltInsDecorators =
decoratorForBuiltIn(Builtins.DUALSTACK), decoratorForBuiltIn(Builtins.DUALSTACK),
decoratorForBuiltIn( decoratorForBuiltIn(
Builtins.SDK_ENDPOINT, Builtins.SDK_ENDPOINT,
ConfigParam("endpoint_url", RuntimeType.String.toSymbol(), endpointUrlDocs), ConfigParam.Builder().name("endpoint_url").type(RuntimeType.String.toSymbol()).setterDocs(endpointUrlDocs),
), ),
).toTypedArray() ).toTypedArray()

View File

@ -13,7 +13,6 @@ 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.letIf
@ -32,9 +31,10 @@ class HttpConnectorDecorator : ClientCodegenDecorator {
} }
class HttpConnectorConfigCustomization( class HttpConnectorConfigCustomization(
codegenContext: CodegenContext, codegenContext: ClientCodegenContext,
) : ConfigCustomization() { ) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val moduleUseName = codegenContext.moduleUseName() private val moduleUseName = codegenContext.moduleUseName()
private val codegenScope = arrayOf( private val codegenScope = arrayOf(
"HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"),
@ -43,18 +43,32 @@ class HttpConnectorConfigCustomization(
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
return when (section) { return when (section) {
is ServiceConfig.ConfigStruct -> writable { is ServiceConfig.ConfigStruct -> writable {
rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) if (runtimeMode.defaultToMiddleware) {
rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope)
}
} }
is ServiceConfig.ConfigImpl -> writable { is ServiceConfig.ConfigImpl -> writable {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. """
pub fn http_connector(&self) -> Option<&#{HttpConnector}> { /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any.
self.http_connector.as_ref() pub fn http_connector(&self) -> Option<&#{HttpConnector}> {
} self.inner.load::<#{HttpConnector}>()
""", }
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any.
pub fn http_connector(&self) -> Option<&#{HttpConnector}> {
self.http_connector.as_ref()
}
""",
*codegenScope,
)
}
} }
is ServiceConfig.BuilderStruct -> writable { is ServiceConfig.BuilderStruct -> writable {
rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope)
@ -145,7 +159,11 @@ class HttpConnectorConfigCustomization(
) )
} }
is ServiceConfig.BuilderBuild -> writable { is ServiceConfig.BuilderBuild -> writable {
rust("http_connector: self.http_connector,") if (runtimeMode.defaultToOrchestrator) {
rust("layer.store_or_unset(self.http_connector);")
} else {
rust("http_connector: self.http_connector,")
}
} }
else -> emptySection else -> emptySection
} }

View File

@ -21,7 +21,6 @@ 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
@ -131,7 +130,11 @@ class RegionDecorator : ClientCodegenDecorator {
override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? { override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? {
return when (parameter.builtIn) { return when (parameter.builtIn) {
Builtins.REGION.builtIn -> writable { Builtins.REGION.builtIn -> writable {
rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) {
rust("$configRef.region().as_ref().map(|r|r.as_ref().to_owned())")
} else {
rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())")
}
} }
else -> null else -> null
} }
@ -153,22 +156,41 @@ class RegionDecorator : ClientCodegenDecorator {
} }
} }
class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val region = region(codegenContext.runtimeConfig) private val region = region(codegenContext.runtimeConfig)
private val moduleUseName = codegenContext.moduleUseName() private val moduleUseName = codegenContext.moduleUseName()
private val runtimeMode = codegenContext.smithyRuntimeMode
private val codegenScope = arrayOf("Region" to region.resolve("Region")) private val codegenScope = arrayOf("Region" to region.resolve("Region"))
override fun section(section: ServiceConfig) = writable { override fun section(section: ServiceConfig) = writable {
when (section) { when (section) {
ServiceConfig.ConfigStruct -> rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) ServiceConfig.ConfigStruct -> {
ServiceConfig.ConfigImpl -> rustTemplate( if (runtimeMode.defaultToMiddleware) {
""" rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope)
/// Returns the AWS region, if it was provided.
pub fn region(&self) -> Option<&#{Region}> {
self.region.as_ref()
} }
""", }
*codegenScope, ServiceConfig.ConfigImpl -> {
) if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
/// Returns the AWS region, if it was provided.
pub fn region(&self) -> Option<&#{Region}> {
self.inner.load::<#{Region}>()
}
""",
*codegenScope,
)
} else {
rustTemplate(
"""
/// Returns the AWS region, if it was provided.
pub fn region(&self) -> Option<&#{Region}> {
self.region.as_ref()
}
""",
*codegenScope,
)
}
}
ServiceConfig.BuilderStruct -> ServiceConfig.BuilderStruct ->
rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope)
@ -201,10 +223,13 @@ class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization
*codegenScope, *codegenScope,
) )
ServiceConfig.BuilderBuild -> rustTemplate( ServiceConfig.BuilderBuild -> {
"""region: self.region,""", if (runtimeMode.defaultToOrchestrator) {
*codegenScope, rust("layer.store_or_unset(self.region);")
) } else {
rust("region: self.region,")
}
}
else -> emptySection else -> emptySection
} }

View File

@ -20,7 +20,7 @@ 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization
import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization
@ -40,7 +40,7 @@ class UserAgentDecorator : ClientCodegenDecorator {
codegenContext: ClientCodegenContext, codegenContext: ClientCodegenContext,
baseCustomizations: List<ConfigCustomization>, baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> { ): List<ConfigCustomization> {
return baseCustomizations + AppNameCustomization(codegenContext.runtimeConfig) return baseCustomizations + AppNameCustomization(codegenContext)
} }
override fun operationCustomizations( override fun operationCustomizations(
@ -149,15 +149,18 @@ class UserAgentDecorator : ClientCodegenDecorator {
} }
} }
private class AppNameCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { private class AppNameCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val codegenScope = arrayOf( private val codegenScope = arrayOf(
*preludeScope,
"AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"),
) )
override fun section(section: ServiceConfig): Writable = override fun section(section: ServiceConfig): Writable =
when (section) { when (section) {
is ServiceConfig.BuilderStruct -> writable { is ServiceConfig.BuilderStruct -> writable {
rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope)
} }
is ServiceConfig.BuilderImpl -> writable { is ServiceConfig.BuilderImpl -> writable {
@ -176,7 +179,7 @@ class UserAgentDecorator : ClientCodegenDecorator {
/// ///
/// This _optional_ name is used to identify the application in the user agent that /// This _optional_ name is used to identify the application in the user agent that
/// gets sent along with requests. /// gets sent along with requests.
pub fn set_app_name(&mut self, app_name: Option<#{AppName}>) -> &mut Self { pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self {
self.app_name = app_name; self.app_name = app_name;
self self
} }
@ -186,26 +189,47 @@ class UserAgentDecorator : ClientCodegenDecorator {
} }
is ServiceConfig.BuilderBuild -> writable { is ServiceConfig.BuilderBuild -> writable {
rust("app_name: self.app_name,") if (runtimeMode.defaultToOrchestrator) {
rust("layer.store_or_unset(self.app_name);")
} else {
rust("app_name: self.app_name,")
}
} }
is ServiceConfig.ConfigStruct -> writable { is ServiceConfig.ConfigStruct -> writable {
rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) if (runtimeMode.defaultToMiddleware) {
rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope)
}
} }
is ServiceConfig.ConfigImpl -> writable { is ServiceConfig.ConfigImpl -> writable {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Returns the name of the app that is using the client, if it was provided. """
/// /// Returns the name of the app that is using the client, if it was provided.
/// This _optional_ name is used to identify the application in the user agent that ///
/// gets sent along with requests. /// This _optional_ name is used to identify the application in the user agent that
pub fn app_name(&self) -> Option<&#{AppName}> { /// gets sent along with requests.
self.app_name.as_ref() pub fn app_name(&self) -> #{Option}<&#{AppName}> {
} self.inner.load::<#{AppName}>()
""", }
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Returns the name of the app that is using the client, if it was provided.
///
/// This _optional_ name is used to identify the application in the user agent that
/// gets sent along with requests.
pub fn app_name(&self) -> #{Option}<&#{AppName}> {
self.app_name.as_ref()
}
""",
*codegenScope,
)
}
} }
else -> emptySection else -> emptySection

View File

@ -52,6 +52,7 @@ class TimestreamDecorator : ClientCodegenDecorator {
Visibility.PUBLIC, Visibility.PUBLIC,
CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")),
) )
val runtimeMode = codegenContext.smithyRuntimeMode
rustCrate.lib { rustCrate.lib {
// helper function to resolve an endpoint given a base client // helper function to resolve an endpoint given a base client
rustTemplate( rustTemplate(
@ -62,7 +63,7 @@ class TimestreamDecorator : ClientCodegenDecorator {
#{ResolveEndpointError}::from_source("failed to call describe_endpoints", e) #{ResolveEndpointError}::from_source("failed to call describe_endpoints", e)
})?; })?;
let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap(); let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap();
let expiry = client.conf().time_source.now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); let expiry = client.conf().time_source().now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60);
Ok(( Ok((
#{Endpoint}::builder() #{Endpoint}::builder()
.url(format!("https://{}", endpoint.address().unwrap())) .url(format!("https://{}", endpoint.address().unwrap()))
@ -78,7 +79,7 @@ class TimestreamDecorator : ClientCodegenDecorator {
pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> {
let mut new_conf = self.conf().clone(); let mut new_conf = self.conf().clone();
let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); let sleep = self.conf().sleep_impl().expect("sleep impl must be provided");
let time = self.conf().time_source.clone(); let time = self.conf().time_source();
let (resolver, reloader) = #{endpoint_discovery}::create_cache( let (resolver, reloader) = #{endpoint_discovery}::create_cache(
move || { move || {
let client = self.clone(); let client = self.clone();
@ -92,7 +93,6 @@ class TimestreamDecorator : ClientCodegenDecorator {
Ok((Self::from_conf(new_conf), reloader)) Ok((Self::from_conf(new_conf), reloader))
} }
} }
""", """,
"endpoint_discovery" to endpointDiscovery.toType(), "endpoint_discovery" to endpointDiscovery.toType(),
"SystemTime" to RuntimeType.std.resolve("time::SystemTime"), "SystemTime" to RuntimeType.std.resolve("time::SystemTime"),

View File

@ -5,12 +5,18 @@
package software.amazon.smithy.rustsdk package software.amazon.smithy.rustsdk
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
internal class CredentialProviderConfigTest { internal class CredentialProviderConfigTest {
@Test @ParameterizedTest
fun `generates a valid config`() { @ValueSource(strings = ["middleware", "orchestrator"])
validateConfigCustomizations(CredentialProviderConfig(AwsTestRuntimeConfig)) fun `generates a valid config`(smithyRuntimeModeStr: String) {
val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode)
validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext.runtimeConfig))
} }
} }

View File

@ -5,15 +5,20 @@
package software.amazon.smithy.rustsdk package software.amazon.smithy.rustsdk
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
class HttpConnectorConfigCustomizationTest { class HttpConnectorConfigCustomizationTest {
@Test @ParameterizedTest
fun `generates a valid config`() { @ValueSource(strings = ["middleware", "orchestrator"])
fun `generates a valid config`(smithyRuntimeModeStr: String) {
val project = TestWorkspace.testProject() val project = TestWorkspace.testProject()
val codegenContext = awsTestCodegenContext() val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
validateConfigCustomizations(HttpConnectorConfigCustomization(codegenContext), project) val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode)
validateConfigCustomizations(codegenContext, HttpConnectorConfigCustomization(codegenContext), project)
} }
} }

View File

@ -5,22 +5,27 @@
package software.amazon.smithy.rustsdk package software.amazon.smithy.rustsdk
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.rustSettings import software.amazon.smithy.rust.codegen.core.testutil.rustSettings
internal class RegionProviderConfigTest { internal class RegionProviderConfigTest {
@Test @ParameterizedTest
fun `generates a valid config`() { @ValueSource(strings = ["middleware", "orchestrator"])
fun `generates a valid config`(smithyRuntimeModeStr: String) {
val project = TestWorkspace.testProject() val project = TestWorkspace.testProject()
val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
val codegenContext = awsTestCodegenContext( val codegenContext = awsTestCodegenContext(
settings = testClientRustSettings( settings = testClientRustSettings(
moduleName = project.rustSettings().moduleName, moduleName = project.rustSettings().moduleName,
runtimeConfig = AwsTestRuntimeConfig, runtimeConfig = AwsTestRuntimeConfig,
), ),
) ).withSmithyRuntimeMode(smithyRuntimeMode)
validateConfigCustomizations(RegionProviderConfig(codegenContext), project) validateConfigCustomizations(codegenContext, RegionProviderConfig(codegenContext), project)
} }
} }

View File

@ -5,19 +5,31 @@
package software.amazon.smithy.rustsdk package software.amazon.smithy.rustsdk
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.aws.traits.auth.SigV4Trait import software.amazon.smithy.aws.traits.auth.SigV4Trait
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject
import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest
internal class SigV4SigningDecoratorTest { internal class SigV4SigningDecoratorTest {
@Test @ParameterizedTest
fun `generates a valid config`() { @ValueSource(strings = ["middleware", "orchestrator"])
fun `generates a valid config`(smithyRuntimeModeStr: String) {
val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
val codegenContext = awsTestCodegenContext(
settings = testClientRustSettings(
runtimeConfig = AwsTestRuntimeConfig,
),
).withSmithyRuntimeMode(smithyRuntimeMode)
val project = stubConfigProject( val project = stubConfigProject(
codegenContext,
SigV4SigningConfig( SigV4SigningConfig(
AwsTestRuntimeConfig, codegenContext.runtimeConfig,
true, true,
SigV4Trait.builder().name("test-service").build(), SigV4Trait.builder().name("test-service").build(),
), ),

View File

@ -25,6 +25,7 @@ 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.letIf
@ -46,7 +47,7 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator {
baseCustomizations: List<ConfigCustomization>, baseCustomizations: List<ConfigCustomization>,
): List<ConfigCustomization> { ): List<ConfigCustomization> {
return baseCustomizations.letIf(applies(codegenContext)) { customizations -> return baseCustomizations.letIf(applies(codegenContext)) { customizations ->
customizations + ApiKeyConfigCustomization(codegenContext.runtimeConfig) customizations + ApiKeyConfigCustomization(codegenContext)
} }
} }
@ -156,15 +157,18 @@ private class ApiKeyOperationCustomization(private val runtimeConfig: RuntimeCon
} }
} }
private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { private class ApiKeyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() {
val runtimeMode = codegenContext.smithyRuntimeMode
val runtimeConfig = codegenContext.runtimeConfig
private val codegenScope = arrayOf( private val codegenScope = arrayOf(
*preludeScope,
"ApiKey" to apiKey(runtimeConfig), "ApiKey" to apiKey(runtimeConfig),
) )
override fun section(section: ServiceConfig): Writable = override fun section(section: ServiceConfig): Writable =
when (section) { when (section) {
is ServiceConfig.BuilderStruct -> writable { is ServiceConfig.BuilderStruct -> writable {
rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope)
} }
is ServiceConfig.BuilderImpl -> writable { is ServiceConfig.BuilderImpl -> writable {
rustTemplate( rustTemplate(
@ -176,7 +180,7 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu
} }
/// Sets the API key that will be used by the client. /// Sets the API key that will be used by the client.
pub fn set_api_key(&mut self, api_key: Option<#{ApiKey}>) -> &mut Self { pub fn set_api_key(&mut self, api_key: #{Option}<#{ApiKey}>) -> &mut Self {
self.api_key = api_key; self.api_key = api_key;
self self
} }
@ -185,21 +189,39 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu
) )
} }
is ServiceConfig.BuilderBuild -> writable { is ServiceConfig.BuilderBuild -> writable {
rust("api_key: self.api_key,") if (runtimeMode.defaultToOrchestrator) {
rust("layer.store_or_unset(self.api_key);")
} else {
rust("api_key: self.api_key,")
}
} }
is ServiceConfig.ConfigStruct -> writable { is ServiceConfig.ConfigStruct -> writable {
rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) if (runtimeMode.defaultToMiddleware) {
rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope)
}
} }
is ServiceConfig.ConfigImpl -> writable { is ServiceConfig.ConfigImpl -> writable {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Returns API key used by the client, if it was provided. """
pub fn api_key(&self) -> Option<&#{ApiKey}> { /// Returns API key used by the client, if it was provided.
self.api_key.as_ref() pub fn api_key(&self) -> #{Option}<&#{ApiKey}> {
} self.inner.load::<#{ApiKey}>()
""", }
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Returns API key used by the client, if it was provided.
pub fn api_key(&self) -> #{Option}<&#{ApiKey}> {
self.api_key.as_ref()
}
""",
*codegenScope,
)
}
} }
else -> emptySection else -> emptySection
} }

View File

@ -237,6 +237,7 @@ private class HttpAuthConfigCustomization(
private val authSchemes: HttpAuthSchemes, private val authSchemes: HttpAuthSchemes,
) : ConfigCustomization() { ) : ConfigCustomization() {
private val codegenScope = codegenScope(codegenContext.runtimeConfig) private val codegenScope = codegenScope(codegenContext.runtimeConfig)
private val runtimeMode = codegenContext.smithyRuntimeMode
override fun section(section: ServiceConfig): Writable = writable { override fun section(section: ServiceConfig): Writable = writable {
when (section) { when (section) {
@ -324,7 +325,9 @@ private class HttpAuthConfigCustomization(
} }
is ServiceConfig.BuilderBuild -> { is ServiceConfig.BuilderBuild -> {
rust("identity_resolvers: self.identity_resolvers,") if (runtimeMode.defaultToMiddleware) {
rust("identity_resolvers: self.identity_resolvers,")
}
} }
is ServiceConfig.ConfigStruct -> { is ServiceConfig.ConfigStruct -> {
@ -343,6 +346,10 @@ private class HttpAuthConfigCustomization(
) )
} }
is ServiceConfig.BuilderBuildExtras -> {
rust("identity_resolvers: self.identity_resolvers,")
}
else -> {} else -> {}
} }
} }

View File

@ -13,7 +13,6 @@ 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.letIf
@ -31,9 +30,10 @@ class HttpConnectorConfigDecorator : ClientCodegenDecorator {
} }
private class HttpConnectorConfigCustomization( private class HttpConnectorConfigCustomization(
codegenContext: CodegenContext, codegenContext: ClientCodegenContext,
) : ConfigCustomization() { ) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val moduleUseName = codegenContext.moduleUseName() private val moduleUseName = codegenContext.moduleUseName()
private val codegenScope = arrayOf( private val codegenScope = arrayOf(
"HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"),
@ -42,19 +42,33 @@ private class HttpConnectorConfigCustomization(
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
return when (section) { return when (section) {
is ServiceConfig.ConfigStruct -> writable { is ServiceConfig.ConfigStruct -> writable {
rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) if (runtimeMode.defaultToMiddleware) {
rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope)
}
} }
is ServiceConfig.ConfigImpl -> writable { is ServiceConfig.ConfigImpl -> writable {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. """
pub fn http_connector(&self) -> Option<&#{HttpConnector}> { /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any.
self.http_connector.as_ref() pub fn http_connector(&self) -> Option<&#{HttpConnector}> {
} self.inner.load::<#{HttpConnector}>()
""", }
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any.
pub fn http_connector(&self) -> Option<&#{HttpConnector}> {
self.http_connector.as_ref()
}
""",
*codegenScope,
)
}
} }
is ServiceConfig.BuilderStruct -> writable { is ServiceConfig.BuilderStruct -> writable {
@ -147,7 +161,11 @@ private class HttpConnectorConfigCustomization(
} }
is ServiceConfig.BuilderBuild -> writable { is ServiceConfig.BuilderBuild -> writable {
rust("http_connector: self.http_connector,") if (runtimeMode.defaultToOrchestrator) {
rust("self.http_connector.map(|c| layer.store_put(c));")
} else {
rust("http_connector: self.http_connector,")
}
} }
else -> emptySection else -> emptySection

View File

@ -26,17 +26,13 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus
writable { writable {
when (section) { when (section) {
ServiceConfig.ConfigStruct -> rustTemplate( ServiceConfig.ConfigStruct -> rustTemplate(
""" "pub(crate) interceptors: Vec<#{SharedInterceptor}>,",
pub(crate) interceptors: Vec<#{SharedInterceptor}>,
""",
*codegenScope, *codegenScope,
) )
ServiceConfig.BuilderStruct -> ServiceConfig.BuilderStruct ->
rustTemplate( rustTemplate(
""" "interceptors: Vec<#{SharedInterceptor}>,",
interceptors: Vec<#{SharedInterceptor}>,
""",
*codegenScope, *codegenScope,
) )
@ -171,18 +167,14 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus
*codegenScope, *codegenScope,
) )
ServiceConfig.BuilderBuild -> rust(
"""
interceptors: self.interceptors,
""",
)
is ServiceConfig.RuntimePluginInterceptors -> rust( is ServiceConfig.RuntimePluginInterceptors -> rust(
""" """
${section.interceptors}.extend(self.interceptors.iter().cloned()); ${section.interceptors}.extend(self.interceptors.iter().cloned());
""", """,
) )
is ServiceConfig.BuilderBuildExtras -> rust("interceptors: self.interceptors,")
else -> emptySection else -> emptySection
} }
} }

View File

@ -5,6 +5,7 @@
package software.amazon.smithy.rust.codegen.client.smithy.customizations package software.amazon.smithy.rust.codegen.client.smithy.customizations
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection
@ -14,13 +15,13 @@ 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCustomization() { class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry") private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry")
private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep") private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep")
private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout")
@ -35,38 +36,64 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust
override fun section(section: ServiceConfig) = override fun section(section: ServiceConfig) =
writable { writable {
when (section) { when (section) {
is ServiceConfig.ConfigStruct -> rustTemplate( is ServiceConfig.ConfigStruct -> {
""" if (runtimeMode.defaultToMiddleware) {
retry_config: Option<#{RetryConfig}>, rustTemplate(
sleep_impl: Option<#{SharedAsyncSleep}>, """
timeout_config: Option<#{TimeoutConfig}>, retry_config: Option<#{RetryConfig}>,
""", sleep_impl: Option<#{SharedAsyncSleep}>,
*codegenScope, timeout_config: Option<#{TimeoutConfig}>,
) """,
*codegenScope,
is ServiceConfig.ConfigImpl -> { )
rustTemplate( }
"""
/// Return a reference to the retry configuration contained in this config, if any.
pub fn retry_config(&self) -> Option<&#{RetryConfig}> {
self.retry_config.as_ref()
}
/// Return a cloned shared async sleep implementation from this config, if any.
pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> {
self.sleep_impl.clone()
}
/// Return a reference to the timeout configuration contained in this config, if any.
pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> {
self.timeout_config.as_ref()
}
""",
*codegenScope,
)
} }
is ServiceConfig.BuilderStruct -> is ServiceConfig.ConfigImpl -> {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
/// Return a reference to the retry configuration contained in this config, if any.
pub fn retry_config(&self) -> Option<&#{RetryConfig}> {
self.inner.load::<#{RetryConfig}>()
}
/// Return a cloned shared async sleep implementation from this config, if any.
pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> {
self.inner.load::<#{SharedAsyncSleep}>().cloned()
}
/// Return a reference to the timeout configuration contained in this config, if any.
pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> {
self.inner.load::<#{TimeoutConfig}>()
}
""",
*codegenScope,
)
} else {
rustTemplate(
"""
/// Return a reference to the retry configuration contained in this config, if any.
pub fn retry_config(&self) -> Option<&#{RetryConfig}> {
self.retry_config.as_ref()
}
/// Return a cloned shared async sleep implementation from this config, if any.
pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> {
self.sleep_impl.clone()
}
/// Return a reference to the timeout configuration contained in this config, if any.
pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> {
self.timeout_config.as_ref()
}
""",
*codegenScope,
)
}
}
is ServiceConfig.BuilderStruct -> {
rustTemplate( rustTemplate(
""" """
retry_config: Option<#{RetryConfig}>, retry_config: Option<#{RetryConfig}>,
@ -75,6 +102,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust
""", """,
*codegenScope, *codegenScope,
) )
}
ServiceConfig.BuilderImpl -> ServiceConfig.BuilderImpl ->
rustTemplate( rustTemplate(
@ -216,21 +244,30 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust
*codegenScope, *codegenScope,
) )
ServiceConfig.BuilderBuild -> rustTemplate( ServiceConfig.BuilderBuild -> {
// We call clone on sleep_impl because the field is used by if (runtimeMode.defaultToOrchestrator) {
// initializing the credentials_cache field later in the build rustTemplate(
// method of a Config builder. """
// We could rearrange the order of decorators so that AwsCodegenDecorator self.retry_config.map(|r| layer.store_put(r));
// runs before RequiredCustomizations, which in turns renders self.sleep_impl.clone().map(|s| layer.store_put(s));
// CredentialsCacheDecorator before this class, but that is a bigger self.timeout_config.map(|t| layer.store_put(t));
// change than adding a call to the clone method on sleep_impl. """,
""" *codegenScope,
retry_config: self.retry_config, )
sleep_impl: self.sleep_impl.clone(), } else {
timeout_config: self.timeout_config, rustTemplate(
""", // We call clone on sleep_impl because the field is used by
*codegenScope, // initializing the credentials_cache field later in the build
) // method of a Config builder.
"""
retry_config: self.retry_config,
sleep_impl: self.sleep_impl.clone(),
timeout_config: self.timeout_config,
""",
*codegenScope,
)
}
}
else -> emptySection else -> emptySection
} }
@ -276,7 +313,7 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz
if let Some(timeout_config) = self.handle.conf.timeout_config() { if let Some(timeout_config) = self.handle.conf.timeout_config() {
${section.newLayerName}.put(timeout_config.clone()); ${section.newLayerName}.put(timeout_config.clone());
} }
${section.newLayerName}.put(self.handle.conf.time_source.clone()); ${section.newLayerName}.put(self.handle.conf.time_source().clone());
""", """,
) )
} }

View File

@ -0,0 +1,127 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.rust.codegen.client.smithy.customizations
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
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.rustBlockTemplate
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.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() {
private val runtimeMode = codegenContext.smithyRuntimeMode
private val codegenScope = arrayOf(
*preludeScope,
"SharedTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource"),
)
override fun section(section: ServiceConfig) =
writable {
when (section) {
is ServiceConfig.ConfigStruct -> {
if (runtimeMode.defaultToMiddleware) {
rustTemplate(
"""
pub(crate) time_source: #{SharedTimeSource},
""",
*codegenScope,
)
}
}
is ServiceConfig.ConfigImpl -> {
rust("/// Return time source used for this service.")
rustBlockTemplate(
"pub fn time_source(&self) -> #{SharedTimeSource}",
*codegenScope,
) {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""self.inner.load::<#{SharedTimeSource}>().expect("time source should be set").clone()""",
*codegenScope,
)
} else {
rust("self.time_source.clone()")
}
}
}
is ServiceConfig.BuilderStruct ->
rustTemplate(
"""
time_source: #{Option}<#{SharedTimeSource}>,
""",
*codegenScope,
)
ServiceConfig.BuilderImpl ->
rustTemplate(
"""
/// Sets the time source used for this service
pub fn time_source(
mut self,
time_source: impl #{Into}<#{SharedTimeSource}>,
) -> Self {
self.time_source = Some(time_source.into());
self
}
/// Sets the time source used for this service
pub fn set_time_source(
&mut self,
time_source: #{Option}<#{SharedTimeSource}>,
) -> &mut Self {
self.time_source = time_source;
self
}
""",
*codegenScope,
)
ServiceConfig.BuilderBuild -> {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
layer.store_put(self.time_source.unwrap_or_default());
""",
*codegenScope,
)
} else {
rustTemplate(
"""
time_source: self.time_source.unwrap_or_default(),
""",
*codegenScope,
)
}
}
else -> emptySection
}
}
}
class TimeSourceOperationCustomization : OperationCustomization() {
override fun section(section: OperationSection): Writable {
return when (section) {
is OperationSection.MutateRequest -> writable {
rust(
"""
${section.request}.properties_mut().insert(${section.config}.time_source.clone());
""",
)
}
else -> emptySection
}
}
}

View File

@ -16,11 +16,11 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Intercep
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceCustomization
import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceOperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.TimeSourceOperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.timeSourceCustomization
import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.Feature
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization
@ -59,9 +59,9 @@ class RequiredCustomizations : ClientCodegenDecorator {
if (codegenContext.smithyRuntimeMode.generateOrchestrator) { if (codegenContext.smithyRuntimeMode.generateOrchestrator) {
baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization(
codegenContext, codegenContext,
) + timeSourceCustomization(codegenContext) ) + TimeSourceCustomization(codegenContext)
} else { } else {
baseCustomizations + ResiliencyConfigCustomization(codegenContext) + timeSourceCustomization(codegenContext) baseCustomizations + ResiliencyConfigCustomization(codegenContext) + TimeSourceCustomization(codegenContext)
} }
override fun libRsCustomizations( override fun libRsCustomizations(

View File

@ -11,19 +11,22 @@ import software.amazon.smithy.model.shapes.ShapeType
import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StringShape
import software.amazon.smithy.rulesengine.traits.ClientContextParamDefinition import software.amazon.smithy.rulesengine.traits.ClientContextParamDefinition
import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.docs
import software.amazon.smithy.rust.codegen.core.rustlang.join import software.amazon.smithy.rust.codegen.core.rustlang.join
import software.amazon.smithy.rust.codegen.core.rustlang.writable 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.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.getTrait
import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.orNull
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
/** /**
@ -32,10 +35,11 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase
* This handles injecting parameters like `s3::Accelerate` or `s3::ForcePathStyle`. The resulting parameters become * This handles injecting parameters like `s3::Accelerate` or `s3::ForcePathStyle`. The resulting parameters become
* setters on the config builder object. * setters on the config builder object.
*/ */
class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomization() { class ClientContextConfigCustomization(ctx: ClientCodegenContext) : ConfigCustomization() {
private val runtimeConfig = ctx.runtimeConfig
private val configParams = ctx.serviceShape.getTrait<ClientContextParamsTrait>()?.parameters.orEmpty().toList() private val configParams = ctx.serviceShape.getTrait<ClientContextParamsTrait>()?.parameters.orEmpty().toList()
.map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider) } .map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider, runtimeConfig) }
private val decorators = configParams.map { standardConfigParam(it) } private val decorators = configParams.map { standardConfigParam(it, ctx) }
companion object { companion object {
fun toSymbol(shapeType: ShapeType, symbolProvider: RustSymbolProvider): Symbol = fun toSymbol(shapeType: ShapeType, symbolProvider: RustSymbolProvider): Symbol =
@ -51,10 +55,13 @@ class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomizatio
name: String, name: String,
definition: ClientContextParamDefinition, definition: ClientContextParamDefinition,
symbolProvider: RustSymbolProvider, symbolProvider: RustSymbolProvider,
runtimeConfig: RuntimeConfig,
): ConfigParam { ): ConfigParam {
val inner = toSymbol(definition.type, symbolProvider)
return ConfigParam( return ConfigParam(
RustReservedWords.escapeIfNeeded(name.toSnakeCase()), RustReservedWords.escapeIfNeeded(name.toSnakeCase()),
toSymbol(definition.type, symbolProvider), inner,
configParamNewtype(RustReservedWords.escapeIfNeeded(name.toPascalCase()), inner, runtimeConfig),
definition.documentation.orNull()?.let { writable { docs(it) } }, definition.documentation.orNull()?.let { writable { docs(it) } },
) )
} }

View File

@ -25,6 +25,7 @@ internal class EndpointConfigCustomization(
ConfigCustomization() { ConfigCustomization() {
private val runtimeConfig = codegenContext.runtimeConfig private val runtimeConfig = codegenContext.runtimeConfig
private val moduleUseName = codegenContext.moduleUseName() private val moduleUseName = codegenContext.moduleUseName()
private val runtimeMode = codegenContext.smithyRuntimeMode
private val types = Types(runtimeConfig) private val types = Types(runtimeConfig)
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
@ -38,21 +39,38 @@ internal class EndpointConfigCustomization(
"Params" to typesGenerator.paramsStruct(), "Params" to typesGenerator.paramsStruct(),
) )
when (section) { when (section) {
is ServiceConfig.ConfigStruct -> rustTemplate( is ServiceConfig.ConfigStruct -> {
"pub (crate) endpoint_resolver: $sharedEndpointResolver,", if (runtimeMode.defaultToMiddleware) {
*codegenScope, rustTemplate(
) "pub (crate) endpoint_resolver: $sharedEndpointResolver,",
*codegenScope,
)
}
}
is ServiceConfig.ConfigImpl -> is ServiceConfig.ConfigImpl -> {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Returns the endpoint resolver. """
pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { /// Returns the endpoint resolver.
self.endpoint_resolver.clone() pub fn endpoint_resolver(&self) -> $sharedEndpointResolver {
} self.inner.load::<$sharedEndpointResolver>().expect("endpoint resolver should be set").clone()
""", }
*codegenScope, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Returns the endpoint resolver.
pub fn endpoint_resolver(&self) -> $sharedEndpointResolver {
self.endpoint_resolver.clone()
}
""",
*codegenScope,
)
}
}
is ServiceConfig.BuilderStruct -> is ServiceConfig.BuilderStruct ->
rustTemplate( rustTemplate(
@ -123,15 +141,27 @@ internal class EndpointConfigCustomization(
ServiceConfig.BuilderBuild -> { ServiceConfig.BuilderBuild -> {
val defaultResolver = typesGenerator.defaultResolver() val defaultResolver = typesGenerator.defaultResolver()
if (defaultResolver != null) { if (defaultResolver != null) {
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| """
#{SharedEndpointResolver}::new(#{DefaultResolver}::new()) layer.store_put(self.endpoint_resolver.unwrap_or_else(||
), #{SharedEndpointResolver}::new(#{DefaultResolver}::new())
""", ));
*codegenScope, """,
"DefaultResolver" to defaultResolver, *codegenScope,
) "DefaultResolver" to defaultResolver,
)
} else {
rustTemplate(
"""
endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||
#{SharedEndpointResolver}::new(#{DefaultResolver}::new())
),
""",
*codegenScope,
"DefaultResolver" to defaultResolver,
)
}
} else { } else {
val alwaysFailsResolver = val alwaysFailsResolver =
RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) { RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) {
@ -152,13 +182,23 @@ internal class EndpointConfigCustomization(
} }
// To keep this diff under control, rather than `.expect` here, insert a resolver that will // To keep this diff under control, rather than `.expect` here, insert a resolver that will
// always fail. In the future, this will be changed to an `expect()` // always fail. In the future, this will be changed to an `expect()`
rustTemplate( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})), """
""", layer.store_put(self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})));
*codegenScope, """,
"FailingResolver" to alwaysFailsResolver, *codegenScope,
) "FailingResolver" to alwaysFailsResolver,
)
} else {
rustTemplate(
"""
endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})),
""",
*codegenScope,
"FailingResolver" to alwaysFailsResolver,
)
}
} }
} }

View File

@ -119,9 +119,9 @@ class EndpointParamsInterceptorGenerator(
val paramName = EndpointParamsGenerator.memberName(name) val paramName = EndpointParamsGenerator.memberName(name)
val setterName = EndpointParamsGenerator.setterName(name) val setterName = EndpointParamsGenerator.setterName(name)
if (param.type == ShapeType.BOOLEAN) { if (param.type == ShapeType.BOOLEAN) {
rust(".$setterName(_config.$paramName)") rust(".$setterName(_config.$paramName())")
} else { } else {
rust(".$setterName(_config.$paramName.clone())") rust(".$setterName(_config.$paramName().clone())")
} }
} }

View File

@ -170,7 +170,7 @@ class ServiceRuntimePluginGenerator(
} }
fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) {
interceptors.extend(self.handle.conf.interceptors.iter().cloned()); interceptors.extend(self.handle.conf.interceptors().cloned());
#{additional_interceptors} #{additional_interceptors}
} }
} }

View File

@ -5,62 +5,98 @@
package software.amazon.smithy.rust.codegen.client.smithy.generators.config package software.amazon.smithy.rust.codegen.client.smithy.generators.config
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.core.rustlang.Writable 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.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization
/** /**
* Add a `make_token` field to Service config. See below for the resulting generated code. * Add a `make_token` field to Service config. See below for the resulting generated code.
*/ */
class IdempotencyTokenProviderCustomization : NamedCustomization<ServiceConfig>() { class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext) : NamedCustomization<ServiceConfig>() {
private val runtimeConfig = codegenContext.runtimeConfig
private val runtimeMode = codegenContext.smithyRuntimeMode
private val codegenScope = arrayOf(
*preludeScope,
"default_provider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("default_provider"),
"IdempotencyTokenProvider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("IdempotencyTokenProvider"),
)
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
return when (section) { return when (section) {
is ServiceConfig.ConfigStruct -> writable { is ServiceConfig.ConfigStruct -> writable {
rust("pub (crate) make_token: #T::IdempotencyTokenProvider,", RuntimeType.IdempotencyToken) if (runtimeMode.defaultToMiddleware) {
rustTemplate("pub (crate) make_token: #{IdempotencyTokenProvider},", *codegenScope)
}
} }
ServiceConfig.ConfigImpl -> writable { ServiceConfig.ConfigImpl -> writable {
rust( if (runtimeMode.defaultToOrchestrator) {
""" rustTemplate(
/// Returns a copy of the idempotency token provider. """
/// If a random token provider was configured, /// Returns a copy of the idempotency token provider.
/// a newly-randomized token provider will be returned. /// If a random token provider was configured,
pub fn make_token(&self) -> #T::IdempotencyTokenProvider { /// a newly-randomized token provider will be returned.
self.make_token.clone() pub fn make_token(&self) -> #{IdempotencyTokenProvider} {
} self.inner.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone()
""", }
RuntimeType.IdempotencyToken, """,
) *codegenScope,
)
} else {
rustTemplate(
"""
/// Returns a copy of the idempotency token provider.
/// If a random token provider was configured,
/// a newly-randomized token provider will be returned.
pub fn make_token(&self) -> #{IdempotencyTokenProvider} {
self.make_token.clone()
}
""",
*codegenScope,
)
}
} }
ServiceConfig.BuilderStruct -> writable { ServiceConfig.BuilderStruct -> writable {
rust("make_token: Option<#T::IdempotencyTokenProvider>,", RuntimeType.IdempotencyToken) rustTemplate("make_token: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope)
} }
ServiceConfig.BuilderImpl -> writable { ServiceConfig.BuilderImpl -> writable {
rustTemplate( rustTemplate(
""" """
/// Sets the idempotency token provider to use for service calls that require tokens. /// Sets the idempotency token provider to use for service calls that require tokens.
pub fn make_token(mut self, make_token: impl Into<#{TokenProvider}>) -> Self { pub fn make_token(mut self, make_token: impl #{Into}<#{IdempotencyTokenProvider}>) -> Self {
self.set_make_token(Some(make_token.into())); self.set_make_token(#{Some}(make_token.into()));
self self
} }
/// Sets the idempotency token provider to use for service calls that require tokens. /// Sets the idempotency token provider to use for service calls that require tokens.
pub fn set_make_token(&mut self, make_token: Option<#{TokenProvider}>) -> &mut Self { pub fn set_make_token(&mut self, make_token: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self {
self.make_token = make_token; self.make_token = make_token;
self self
} }
""", """,
"TokenProvider" to RuntimeType.IdempotencyToken.resolve("IdempotencyTokenProvider"), *codegenScope,
) )
} }
ServiceConfig.BuilderBuild -> writable { ServiceConfig.BuilderBuild -> writable {
rust("make_token: self.make_token.unwrap_or_else(#T::default_provider),", RuntimeType.IdempotencyToken) if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"layer.store_put(self.make_token.unwrap_or_else(#{default_provider}));",
*codegenScope,
)
} else {
rustTemplate(
"make_token: self.make_token.unwrap_or_else(#{default_provider}),",
*codegenScope,
)
}
} }
is ServiceConfig.DefaultForTests -> writable { is ServiceConfig.DefaultForTests -> writable {
@ -71,41 +107,3 @@ class IdempotencyTokenProviderCustomization : NamedCustomization<ServiceConfig>(
} }
} }
} }
/* Generated Code
pub struct Config {
pub(crate) make_token: Box<dyn crate::idempotency_token::MakeIdempotencyToken>,
}
impl Config {
pub fn builder() -> Builder {
Builder::default()
}
}
#[derive(Default)]
pub struct Builder {
#[allow(dead_code)]
make_token: Option<Box<dyn crate::idempotency_token::MakeIdempotencyToken>>,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
/// Sets the idempotency token provider to use for service calls that require tokens.
pub fn make_token(
mut self,
make_token: impl crate::idempotency_token::MakeIdempotencyToken + 'static,
) -> Self {
self.make_token = Some(Box::new(make_token));
self
}
pub fn build(self) -> Config {
Config {
make_token: self
.make_token
.unwrap_or_else(|| Box::new(crate::idempotency_token::default_provider())),
}
}
}
*/

View File

@ -11,6 +11,9 @@ import software.amazon.smithy.model.knowledge.OperationIndex
import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.knowledge.TopDownIndex
import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.traits.IdempotencyTokenTrait import software.amazon.smithy.model.traits.IdempotencyTokenTrait
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.customizations.codegenScope
import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
@ -22,7 +25,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate 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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope
import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization
@ -89,6 +92,14 @@ sealed class ServiceConfig(name: String) : Section(name) {
*/ */
object BuilderBuild : ServiceConfig("BuilderBuild") object BuilderBuild : ServiceConfig("BuilderBuild")
// TODO(enableNewSmithyRuntime): This is temporary until config builder is backed by a CloneableLayer.
// It is needed because certain config fields appear explicitly regardless of the smithy runtime mode, e.g.
// interceptors. The [BuilderBuild] section is bifurcated depending on the runtime mode (in the orchestrator mode,
// storing a field into a frozen layer and in the middleware moving it into a corresponding service config field)
// so we need a different temporary section to always move a field from a builder to service config within the
// build method.
object BuilderBuildExtras : ServiceConfig("BuilderBuildExtras")
/** /**
* A section for setting up a field to be used by RuntimePlugin * A section for setting up a field to be used by RuntimePlugin
*/ */
@ -110,10 +121,53 @@ sealed class ServiceConfig(name: String) : Section(name) {
data class ConfigParam( data class ConfigParam(
val name: String, val name: String,
val type: Symbol, val type: Symbol,
val newtype: RuntimeType?,
val setterDocs: Writable?, val setterDocs: Writable?,
val getterDocs: Writable? = null, val getterDocs: Writable? = null,
val optional: Boolean = true, val optional: Boolean = true,
) ) {
data class Builder(
var name: String? = null,
var type: Symbol? = null,
var newtype: RuntimeType? = null,
var setterDocs: Writable? = null,
var getterDocs: Writable? = null,
var optional: Boolean = true,
) {
fun name(name: String) = apply { this.name = name }
fun type(type: Symbol) = apply { this.type = type }
fun newtype(newtype: RuntimeType) = apply { this.newtype = newtype }
fun setterDocs(setterDocs: Writable?) = apply { this.setterDocs = setterDocs }
fun getterDocs(getterDocs: Writable?) = apply { this.getterDocs = getterDocs }
fun optional(optional: Boolean) = apply { this.optional = optional }
fun build() = ConfigParam(name!!, type!!, newtype, setterDocs, getterDocs, optional)
}
}
/**
* Generate a [RuntimeType] for a newtype whose name is [newtypeName] that wraps [inner].
*
* When config parameters are stored in a config map in Rust, stored parameters are keyed by type.
* Therefore, primitive types, such as bool and String, need to be wrapped in newtypes to make them distinct.
*/
fun configParamNewtype(newtypeName: String, inner: Symbol, runtimeConfig: RuntimeConfig) =
RuntimeType.forInlineFun(newtypeName, ClientRustModule.Config) {
val codegenScope = arrayOf(
"Storable" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Storable"),
"StoreReplace" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::StoreReplace"),
)
rustTemplate(
"""
##[derive(Debug, Clone)]
pub(crate) struct $newtypeName($inner);
impl #{Storable} for $newtypeName {
type Storer = #{StoreReplace}<$newtypeName>;
}
""",
*codegenScope,
)
}
/** /**
* Config customization for a config param with no special behavior: * Config customization for a config param with no special behavior:
@ -121,19 +175,47 @@ data class ConfigParam(
* 2. convenience setter (non-optional) * 2. convenience setter (non-optional)
* 3. standard setter (&mut self) * 3. standard setter (&mut self)
*/ */
fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : ConfigCustomization() { fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext): ConfigCustomization = object : ConfigCustomization() {
private val runtimeMode = codegenContext.smithyRuntimeMode
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
return when (section) { return when (section) {
is ServiceConfig.ConfigStruct -> writable { ServiceConfig.ConfigStruct -> writable {
docsOrFallback(param.getterDocs) if (runtimeMode.defaultToMiddleware) {
val t = when (param.optional) { docsOrFallback(param.getterDocs)
true -> param.type.makeOptional() val t = when (param.optional) {
false -> param.type true -> param.type.makeOptional()
false -> param.type
}
rust("pub (crate) ${param.name}: #T,", t)
}
}
ServiceConfig.ConfigImpl -> writable {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
pub(crate) fn ${param.name}(&self) -> #{output} {
self.inner.load::<#{newtype}>().map(#{f})
}
""",
"f" to writable {
if (param.type.name == "bool") {
rust("|ty| ty.0")
} else {
rust("|ty| ty.0.clone()")
}
},
"newtype" to param.newtype!!,
"output" to if (param.optional) {
param.type.makeOptional()
} else {
param.type
},
)
} }
rust("pub (crate) ${param.name}: #T,", t)
} }
ServiceConfig.ConfigImpl -> emptySection
ServiceConfig.BuilderStruct -> writable { ServiceConfig.BuilderStruct -> writable {
rust("${param.name}: #T,", param.type.makeOptional()) rust("${param.name}: #T,", param.type.makeOptional())
} }
@ -162,8 +244,15 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf
} }
ServiceConfig.BuilderBuild -> writable { ServiceConfig.BuilderBuild -> writable {
val default = "".letIf(!param.optional) { ".unwrap_or_default() " } if (runtimeMode.defaultToOrchestrator) {
rust("${param.name}: self.${param.name}$default,") rustTemplate(
"layer.store_or_unset(self.${param.name}.map(#{newtype}));",
"newtype" to param.newtype!!,
)
} else {
val default = "".letIf(!param.optional) { ".unwrap_or_default() " }
rust("${param.name}: self.${param.name}$default,")
}
} }
is ServiceConfig.RuntimePluginConfig -> emptySection is ServiceConfig.RuntimePluginConfig -> emptySection
@ -199,19 +288,19 @@ typealias ConfigCustomization = NamedCustomization<ServiceConfig>
* // builder implementation * // builder implementation
* } * }
*/ */
class ServiceConfigGenerator( class ServiceConfigGenerator(
private val codegenContext: CodegenContext, private val codegenContext: ClientCodegenContext,
private val customizations: List<ConfigCustomization> = listOf(), private val customizations: List<ConfigCustomization> = listOf(),
) { ) {
companion object { companion object {
fun withBaseBehavior( fun withBaseBehavior(
codegenContext: CodegenContext, codegenContext: ClientCodegenContext,
extraCustomizations: List<ConfigCustomization>, extraCustomizations: List<ConfigCustomization>,
): ServiceConfigGenerator { ): ServiceConfigGenerator {
val baseFeatures = mutableListOf<ConfigCustomization>() val baseFeatures = mutableListOf<ConfigCustomization>()
if (codegenContext.serviceShape.needsIdempotencyToken(codegenContext.model)) { if (codegenContext.serviceShape.needsIdempotencyToken(codegenContext.model)) {
baseFeatures.add(IdempotencyTokenProviderCustomization()) baseFeatures.add(IdempotencyTokenProviderCustomization(codegenContext))
} }
return ServiceConfigGenerator(codegenContext, baseFeatures + extraCustomizations) return ServiceConfigGenerator(codegenContext, baseFeatures + extraCustomizations)
} }
@ -228,6 +317,8 @@ class ServiceConfigGenerator(
"RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"),
*preludeScope, *preludeScope,
) )
private val moduleUseName = codegenContext.moduleUseName()
private val runtimeMode = codegenContext.smithyRuntimeMode
fun render(writer: RustWriter) { fun render(writer: RustWriter) {
writer.docs("Service config.\n") writer.docs("Service config.\n")
@ -236,6 +327,12 @@ class ServiceConfigGenerator(
} }
Attribute(Attribute.derive(RuntimeType.Clone)).render(writer) Attribute(Attribute.derive(RuntimeType.Clone)).render(writer)
writer.rustBlock("pub struct Config") { writer.rustBlock("pub struct Config") {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"inner: #{FrozenLayer},",
*codegenScope,
)
}
customizations.forEach { customizations.forEach {
it.section(ServiceConfig.ConfigStruct)(this) it.section(ServiceConfig.ConfigStruct)(this)
} }
@ -287,7 +384,7 @@ class ServiceConfigGenerator(
writer.rustBlock("impl Builder") { writer.rustBlock("impl Builder") {
writer.docs("Constructs a config builder.") writer.docs("Constructs a config builder.")
writer.rustTemplate("pub fn new() -> Self { Self::default() }") writer.rust("pub fn new() -> Self { Self::default() }")
customizations.forEach { customizations.forEach {
it.section(ServiceConfig.BuilderImpl)(this) it.section(ServiceConfig.BuilderImpl)(this)
} }
@ -312,15 +409,31 @@ class ServiceConfigGenerator(
docs("Builds a [`Config`].") docs("Builds a [`Config`].")
rustBlock("pub fn build(self) -> Config") { rustBlock("pub fn build(self) -> Config") {
rustBlock("Config") { if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""let mut layer = #{Layer}::new("$moduleUseName::Config");""",
*codegenScope,
)
customizations.forEach { customizations.forEach {
it.section(ServiceConfig.BuilderBuild)(this) it.section(ServiceConfig.BuilderBuild)(this)
} }
rustBlock("Config") {
customizations.forEach {
it.section(ServiceConfig.BuilderBuildExtras)(this)
}
rust("inner: layer.freeze(),")
}
} else {
rustBlock("Config") {
customizations.forEach {
it.section(ServiceConfig.BuilderBuild)(this)
}
}
} }
} }
} customizations.forEach {
customizations.forEach { it.section(ServiceConfig.Extras)(writer)
it.section(ServiceConfig.Extras)(writer) }
} }
} }

View File

@ -1,40 +0,0 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.rust.codegen.client.smithy.generators.config
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.docs
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.RuntimeType
fun timeSourceCustomization(codegenContext: ClientCodegenContext) = standardConfigParam(
ConfigParam(
"time_source",
RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource").toSymbol(),
setterDocs = writable { docs("""Sets the time source used for this service""") },
optional = false,
),
)
class TimeSourceOperationCustomization : OperationCustomization() {
override fun section(section: OperationSection): Writable {
return when (section) {
is OperationSection.MutateRequest -> writable {
rust(
"""
${section.request}.properties_mut().insert(${section.config}.time_source.clone());
""",
)
}
else -> emptySection
}
}
}

View File

@ -5,36 +5,61 @@
package software.amazon.smithy.rust.codegen.client.testutil package software.amazon.smithy.rust.codegen.client.testutil
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator
import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype
import software.amazon.smithy.rust.codegen.core.rustlang.Writable 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.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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
/** /**
* Test helper to produce a valid config customization to test that a [ConfigCustomization] can be used in conjunction * Test helper to produce a valid config customization to test that a [ConfigCustomization] can be used in conjunction
* with other [ConfigCustomization]s. * with other [ConfigCustomization]s.
*/ */
fun stubConfigCustomization(name: String): ConfigCustomization { fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ConfigCustomization {
return object : ConfigCustomization() { return object : ConfigCustomization() {
override fun section(section: ServiceConfig): Writable = writable { override fun section(section: ServiceConfig): Writable = writable {
when (section) { when (section) {
ServiceConfig.ConfigStruct -> rust("_$name: u64,") ServiceConfig.ConfigStruct -> {
ServiceConfig.ConfigImpl -> rust( if (codegenContext.smithyRuntimeMode.defaultToMiddleware) {
""" rust("_$name: u64,")
##[allow(missing_docs)]
pub fn $name(&self) -> u64 {
self._$name
} }
""", }
) ServiceConfig.ConfigImpl -> {
if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) {
rustTemplate(
"""
##[allow(missing_docs)]
pub fn $name(&self) -> u64 {
self.inner.load::<#{T}>().map(|u| u.0).unwrap()
}
""",
"T" to configParamNewtype(
"_$name".toPascalCase(), RuntimeType.U64.toSymbol(),
codegenContext.runtimeConfig,
),
)
} else {
rust(
"""
##[allow(missing_docs)]
pub fn $name(&self) -> u64 {
self._$name
}
""",
)
}
}
ServiceConfig.BuilderStruct -> rust("_$name: Option<u64>,") ServiceConfig.BuilderStruct -> rust("_$name: Option<u64>,")
ServiceConfig.BuilderImpl -> rust( ServiceConfig.BuilderImpl -> rust(
""" """
@ -45,11 +70,25 @@ fun stubConfigCustomization(name: String): ConfigCustomization {
} }
""", """,
) )
ServiceConfig.BuilderBuild -> rust( ServiceConfig.BuilderBuild -> {
""" if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) {
_$name: self._$name.unwrap_or(123), rustTemplate(
""", """
) layer.store_or_unset(self._$name.map(#{T}));
""",
"T" to configParamNewtype(
"_$name".toPascalCase(), RuntimeType.U64.toSymbol(),
codegenContext.runtimeConfig,
),
)
} else {
rust(
"""
_$name: self._$name.unwrap_or(123),
""",
)
}
}
else -> emptySection else -> emptySection
} }
} }
@ -63,18 +102,19 @@ fun stubConfigCustomization(name: String): ConfigCustomization {
* */ * */
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
fun validateConfigCustomizations( fun validateConfigCustomizations(
codegenContext: ClientCodegenContext,
customization: ConfigCustomization, customization: ConfigCustomization,
project: TestWriterDelegator? = null, project: TestWriterDelegator? = null,
): TestWriterDelegator { ): TestWriterDelegator {
val project = project ?: TestWorkspace.testProject() val project = project ?: TestWorkspace.testProject()
stubConfigProject(customization, project) stubConfigProject(codegenContext, customization, project)
project.compileAndTest() project.compileAndTest()
return project return project
} }
fun stubConfigProject(customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { fun stubConfigProject(codegenContext: ClientCodegenContext, customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator {
val customizations = listOf(stubConfigCustomization("a")) + customization + stubConfigCustomization("b") val customizations = listOf(stubConfigCustomization("a", codegenContext)) + customization + stubConfigCustomization("b", codegenContext)
val generator = ServiceConfigGenerator(testClientCodegenContext("namespace test".asSmithyModel()), customizations = customizations.toList()) val generator = ServiceConfigGenerator(codegenContext, customizations = customizations.toList())
project.withModule(ClientRustModule.Config) { project.withModule(ClientRustModule.Config) {
generator.render(this) generator.render(this)
unitTest( unitTest(

View File

@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientModuleProvider import software.amazon.smithy.rust.codegen.client.smithy.ClientModuleProvider
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings
import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
@ -90,6 +91,9 @@ fun testClientCodegenContext(
rootDecorator ?: CombinedClientCodegenDecorator(emptyList()), rootDecorator ?: CombinedClientCodegenDecorator(emptyList()),
) )
fun ClientCodegenContext.withSmithyRuntimeMode(smithyRuntimeMode: SmithyRuntimeMode): ClientCodegenContext =
copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableNewSmithyRuntime = smithyRuntimeMode)))
fun TestWriterDelegator.clientRustSettings() = fun TestWriterDelegator.clientRustSettings() =
testClientRustSettings( testClientRustSettings(
service = ShapeId.from("fake#Fake"), service = ShapeId.from("fake#Fake"),

View File

@ -39,7 +39,7 @@ internal class ResiliencyConfigCustomizationTest {
val project = TestWorkspace.testProject(model, ClientCodegenConfig()) val project = TestWorkspace.testProject(model, ClientCodegenConfig())
val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings())
stubConfigProject(ResiliencyConfigCustomization(codegenContext), project) stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project)
ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project) ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project)
project.compileAndTest() project.compileAndTest()
} }

View File

@ -5,9 +5,12 @@
package software.amazon.smithy.rust.codegen.client.smithy.endpoint package software.amazon.smithy.rust.codegen.client.smithy.endpoint
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
@ -29,28 +32,51 @@ class ClientContextConfigCustomizationTest {
service TestService { operations: [] } service TestService { operations: [] }
""".asSmithyModel() """.asSmithyModel()
@Test @ParameterizedTest
fun `client params generate a valid customization`() { @ValueSource(strings = ["middleware", "orchestrator"])
fun `client params generate a valid customization`(smithyRuntimeModeStr: String) {
val project = TestWorkspace.testProject() val project = TestWorkspace.testProject()
val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
project.unitTest { project.unitTest {
rust( if (smithyRuntimeMode.defaultToOrchestrator) {
""" rust(
let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); """
assert_eq!(conf.a_string_param.unwrap(), "hello!"); let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build();
assert_eq!(conf.a_bool_param, Some(true)); assert_eq!(conf.a_string_param().unwrap(), "hello!");
""", assert_eq!(conf.a_bool_param(), Some(true));
) """,
)
} else {
rust(
"""
let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build();
assert_eq!(conf.a_string_param.unwrap(), "hello!");
assert_eq!(conf.a_bool_param, Some(true));
""",
)
}
} }
// unset fields // unset fields
project.unitTest { project.unitTest {
rust( if (smithyRuntimeMode.defaultToOrchestrator) {
""" rust(
let conf = crate::Config::builder().a_string_param("hello!").build(); """
assert_eq!(conf.a_string_param.unwrap(), "hello!"); let conf = crate::Config::builder().a_string_param("hello!").build();
assert_eq!(conf.a_bool_param, None); assert_eq!(conf.a_string_param().unwrap(), "hello!");
""", assert_eq!(conf.a_bool_param(), None);
) """,
)
} else {
rust(
"""
let conf = crate::Config::builder().a_string_param("hello!").build();
assert_eq!(conf.a_string_param.unwrap(), "hello!");
assert_eq!(conf.a_bool_param, None);
""",
)
}
} }
validateConfigCustomizations(ClientContextConfigCustomization(testClientCodegenContext(model)), project) val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode)
validateConfigCustomizations(context, ClientContextConfigCustomization(context), project)
} }
} }

View File

@ -5,12 +5,24 @@
package software.amazon.smithy.rust.codegen.client.smithy.generators.config package software.amazon.smithy.rust.codegen.client.smithy.generators.config
import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
class IdempotencyTokenProviderCustomizationTest { class IdempotencyTokenProviderCustomizationTest {
@Test @ParameterizedTest
fun `generates a valid config`() { @ValueSource(strings = ["middleware", "orchestrator"])
validateConfigCustomizations(IdempotencyTokenProviderCustomization()) fun `generates a valid config`(smithyRuntimeModeStr: String) {
val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
val model = "namespace test".asSmithyModel()
val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode)
validateConfigCustomizations(
codegenContext,
IdempotencyTokenProviderCustomization(codegenContext),
)
} }
} }

View File

@ -7,18 +7,26 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule
import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode
import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext
import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode
import software.amazon.smithy.rust.codegen.core.rustlang.Writable 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.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.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest
import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.lookup
import software.amazon.smithy.rust.codegen.core.util.toPascalCase
internal class ServiceConfigGeneratorTest { internal class ServiceConfigGeneratorTest {
@Test @Test
@ -76,46 +84,98 @@ internal class ServiceConfigGeneratorTest {
model.lookup<ServiceShape>("com.example#ResourceService").needsIdempotencyToken(model) shouldBe true model.lookup<ServiceShape>("com.example#ResourceService").needsIdempotencyToken(model) shouldBe true
} }
@Test @ParameterizedTest
fun `generate customizations as specified`() { @ValueSource(strings = ["middleware", "orchestrator"])
class ServiceCustomizer : NamedCustomization<ServiceConfig>() { fun `generate customizations as specified`(smithyRuntimeModeStr: String) {
class ServiceCustomizer(private val codegenContext: ClientCodegenContext) :
NamedCustomization<ServiceConfig>() {
private val runtimeMode = codegenContext.smithyRuntimeMode
override fun section(section: ServiceConfig): Writable { override fun section(section: ServiceConfig): Writable {
return when (section) { return when (section) {
ServiceConfig.ConfigStructAdditionalDocs -> emptySection ServiceConfig.ConfigStructAdditionalDocs -> emptySection
ServiceConfig.ConfigStruct -> writable { rust("config_field: u64,") } ServiceConfig.ConfigStruct -> writable {
ServiceConfig.ConfigImpl -> writable { if (runtimeMode.defaultToMiddleware) {
rust( rust("config_field: u64,")
""" }
pub fn config_field(&self) -> u64 {
self.config_field
}
""",
)
} }
ServiceConfig.ConfigImpl -> writable {
if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"""
##[allow(missing_docs)]
pub fn config_field(&self) -> u64 {
self.inner.load::<#{T}>().map(|u| u.0).unwrap()
}
""",
"T" to configParamNewtype(
"config_field".toPascalCase(), RuntimeType.U64.toSymbol(),
codegenContext.runtimeConfig,
),
)
} else {
rust(
"""
##[allow(missing_docs)]
pub fn config_field(&self) -> u64 {
self.config_field
}
""",
)
}
}
ServiceConfig.BuilderStruct -> writable { rust("config_field: Option<u64>") } ServiceConfig.BuilderStruct -> writable { rust("config_field: Option<u64>") }
ServiceConfig.BuilderImpl -> emptySection ServiceConfig.BuilderImpl -> emptySection
ServiceConfig.BuilderBuild -> writable { ServiceConfig.BuilderBuild -> writable {
rust("config_field: self.config_field.unwrap_or_default(),") if (runtimeMode.defaultToOrchestrator) {
rustTemplate(
"layer.store_or_unset(self.config_field.map(#{T}));",
"T" to configParamNewtype(
"config_field".toPascalCase(), RuntimeType.U64.toSymbol(),
codegenContext.runtimeConfig,
),
)
} else {
rust("config_field: self.config_field.unwrap_or_default(),")
}
} }
else -> emptySection else -> emptySection
} }
} }
} }
val ctx = testClientCodegenContext()
val sut = ServiceConfigGenerator(ctx, listOf(ServiceCustomizer())) val model = "namespace empty".asSmithyModel()
val symbolProvider = ctx.symbolProvider val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr)
val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode)
val sut = ServiceConfigGenerator(codegenContext, listOf(ServiceCustomizer(codegenContext)))
val symbolProvider = codegenContext.symbolProvider
val project = TestWorkspace.testProject(symbolProvider) val project = TestWorkspace.testProject(symbolProvider)
project.withModule(ClientRustModule.Config) { project.withModule(ClientRustModule.Config) {
sut.render(this) sut.render(this)
unitTest( if (smithyRuntimeMode.defaultToOrchestrator) {
"set_config_fields", unitTest(
""" "set_config_fields",
let mut builder = Config::builder(); """
builder.config_field = Some(99); let mut builder = Config::builder();
let config = builder.build(); builder.config_field = Some(99);
assert_eq!(config.config_field, 99); let config = builder.build();
""", assert_eq!(config.config_field(), 99);
) """,
)
} else {
unitTest(
"set_config_fields",
"""
let mut builder = Config::builder();
builder.config_field = Some(99);
let config = builder.build();
assert_eq!(config.config_field, 99);
""",
)
}
} }
project.compileAndTest() project.compileAndTest()
} }

View File

@ -104,8 +104,12 @@ class InlineDependency(
CargoDependency.Http, CargoDependency.Http,
) )
fun idempotencyToken() = fun idempotencyToken(runtimeConfig: RuntimeConfig) =
forInlineableRustFile("idempotency_token", CargoDependency.FastRand) forInlineableRustFile(
"idempotency_token",
CargoDependency.FastRand,
CargoDependency.smithyTypes(runtimeConfig),
)
fun ec2QueryErrors(runtimeConfig: RuntimeConfig): InlineDependency = fun ec2QueryErrors(runtimeConfig: RuntimeConfig): InlineDependency =
forInlineableRustFile("ec2_query_errors", CargoDependency.smithyXml(runtimeConfig)) forInlineableRustFile("ec2_query_errors", CargoDependency.smithyXml(runtimeConfig))
@ -232,7 +236,8 @@ data class CargoDependency(
val AsyncStream: CargoDependency = CargoDependency("async-stream", CratesIo("0.3.0"), DependencyScope.Dev) val AsyncStream: CargoDependency = CargoDependency("async-stream", CratesIo("0.3.0"), DependencyScope.Dev)
val Criterion: CargoDependency = CargoDependency("criterion", CratesIo("0.4.0"), DependencyScope.Dev) val Criterion: CargoDependency = CargoDependency("criterion", CratesIo("0.4.0"), DependencyScope.Dev)
val FuturesCore: CargoDependency = CargoDependency("futures-core", CratesIo("0.3.25"), DependencyScope.Dev) val FuturesCore: CargoDependency = CargoDependency("futures-core", CratesIo("0.3.25"), DependencyScope.Dev)
val FuturesUtil: CargoDependency = CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false) val FuturesUtil: CargoDependency =
CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false)
val HdrHistogram: CargoDependency = CargoDependency("hdrhistogram", CratesIo("7.5.2"), DependencyScope.Dev) val HdrHistogram: CargoDependency = CargoDependency("hdrhistogram", CratesIo("7.5.2"), DependencyScope.Dev)
val Hound: CargoDependency = CargoDependency("hound", CratesIo("3.4.0"), DependencyScope.Dev) val Hound: CargoDependency = CargoDependency("hound", CratesIo("3.4.0"), DependencyScope.Dev)
val PrettyAssertions: CargoDependency = val PrettyAssertions: CargoDependency =

View File

@ -274,6 +274,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
val String = std.resolve("string::String") val String = std.resolve("string::String")
val Sync = std.resolve("marker::Sync") val Sync = std.resolve("marker::Sync")
val TryFrom = stdConvert.resolve("TryFrom") val TryFrom = stdConvert.resolve("TryFrom")
val U64 = std.resolve("primitive::u64")
val Vec = std.resolve("vec::Vec") val Vec = std.resolve("vec::Vec")
// external cargo dependency types // external cargo dependency types
@ -431,7 +432,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) = fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) =
forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig)) forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig))
val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } fun idempotencyToken(runtimeConfig: RuntimeConfig) =
forInlineDependency(InlineDependency.idempotencyToken(runtimeConfig))
fun runtimePlugin(runtimeConfig: RuntimeConfig) = fun runtimePlugin(runtimeConfig: RuntimeConfig) =
RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin")

View File

@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest
import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest
@ -32,20 +33,23 @@ internal class InlineDependencyTest {
@Test @Test
fun `locate dependencies from the inlineable module`() { fun `locate dependencies from the inlineable module`() {
val dep = InlineDependency.idempotencyToken() val runtimeConfig = TestRuntimeConfig
val dep = InlineDependency.serializationSettings(runtimeConfig)
val testProject = TestWorkspace.testProject() val testProject = TestWorkspace.testProject()
testProject.lib { testProject.lib {
rustTemplate( rustTemplate(
""" """
##[test] ##[test]
fn idempotency_works() { fn header_serialization_settings_can_be_constructed() {
use #{idempotency}::uuid_v4; use #{serialization_settings}::HeaderSerializationSettings;
let res = uuid_v4(0); use #{aws_smithy_http}::header::set_request_header_if_absent;
assert_eq!(res, "00000000-0000-4000-8000-000000000000"); let _settings = HeaderSerializationSettings::default();
} }
""", """,
"idempotency" to dep.toType(), "serialization_settings" to dep.toType(),
"aws_smithy_http" to RuntimeType.smithyHttp(runtimeConfig),
) )
} }
testProject.compileAndTest() testProject.compileAndTest()

View File

@ -12,6 +12,7 @@ rt-tokio = ["tokio/time"]
test-util = [] test-util = []
[dependencies] [dependencies]
aws-smithy-types = { path = "../aws-smithy-types" }
pin-project-lite = "0.2" pin-project-lite = "0.2"
tokio = { version = "1.23.1", features = ["sync"] } tokio = { version = "1.23.1", features = ["sync"] }
tokio-stream = { version = "0.1.5", default-features = false } tokio-stream = { version = "0.1.5", default-features = false }

View File

@ -1,4 +1,8 @@
allowed_external_types = [ allowed_external_types = [
"aws_smithy_types::config_bag::storable::Storable",
"aws_smithy_types::config_bag::storable::StoreReplace",
"aws_smithy_types::config_bag::storable::Storer",
# TODO(https://github.com/awslabs/smithy-rs/issues/1193): Switch to AsyncIterator once standardized # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Switch to AsyncIterator once standardized
"futures_core::stream::Stream", "futures_core::stream::Stream",

View File

@ -6,6 +6,7 @@
//! Provides an [`AsyncSleep`] trait that returns a future that sleeps for a given duration, //! Provides an [`AsyncSleep`] trait that returns a future that sleeps for a given duration,
//! and implementations of `AsyncSleep` for different async runtimes. //! and implementations of `AsyncSleep` for different async runtimes.
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
@ -68,6 +69,10 @@ impl AsyncSleep for SharedAsyncSleep {
} }
} }
impl Storable for SharedAsyncSleep {
type Storer = StoreReplace<SharedAsyncSleep>;
}
#[cfg(feature = "rt-tokio")] #[cfg(feature = "rt-tokio")]
/// Returns a default sleep implementation based on the features enabled /// Returns a default sleep implementation based on the features enabled
pub fn default_async_sleep() -> Option<SharedAsyncSleep> { pub fn default_async_sleep() -> Option<SharedAsyncSleep> {

View File

@ -24,7 +24,7 @@ pub struct ManualTimeSource {
impl TimeSource for ManualTimeSource { impl TimeSource for ManualTimeSource {
fn now(&self) -> SystemTime { fn now(&self) -> SystemTime {
self.start_time + self.log.lock().unwrap().iter().sum() self.start_time + self.log.lock().unwrap().iter().sum::<Duration>()
} }
} }

View File

@ -4,6 +4,7 @@
*/ */
//! Time source abstraction to support WASM and testing //! Time source abstraction to support WASM and testing
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
@ -86,3 +87,7 @@ impl TimeSource for SharedTimeSource {
self.0.now() self.0.now()
} }
} }
impl Storable for SharedTimeSource {
type Storer = StoreReplace<SharedTimeSource>;
}

View File

@ -8,6 +8,7 @@
use crate::erase::DynConnector; use crate::erase::DynConnector;
use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::rt::sleep::SharedAsyncSleep;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use aws_smithy_types::timeout::TimeoutConfig; use aws_smithy_types::timeout::TimeoutConfig;
use std::time::Duration; use std::time::Duration;
use std::{fmt::Debug, sync::Arc}; use std::{fmt::Debug, sync::Arc};
@ -41,6 +42,10 @@ impl Debug for HttpConnector {
} }
} }
impl Storable for HttpConnector {
type Storer = StoreReplace<HttpConnector>;
}
impl HttpConnector { impl HttpConnector {
/// If `HttpConnector` is `Prebuilt`, return a clone of that connector. /// If `HttpConnector` is `Prebuilt`, return a clone of that connector.
/// If `HttpConnector` is `ConnectorFn`, generate a new connector from settings and return it. /// If `HttpConnector` is `ConnectorFn`, generate a new connector from settings and return it.

View File

@ -7,6 +7,7 @@
use crate::endpoint::error::InvalidEndpointError; use crate::endpoint::error::InvalidEndpointError;
use crate::operation::error::BuildError; use crate::operation::error::BuildError;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use http::uri::{Authority, Uri}; use http::uri::{Authority, Uri};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
@ -66,6 +67,10 @@ impl<T> From<Arc<dyn ResolveEndpoint<T>>> for SharedEndpointResolver<T> {
} }
} }
impl<T: 'static> Storable for SharedEndpointResolver<T> {
type Storer = StoreReplace<SharedEndpointResolver<T>>;
}
impl<T> ResolveEndpoint<T> for SharedEndpointResolver<T> { impl<T> ResolveEndpoint<T> for SharedEndpointResolver<T> {
fn resolve_endpoint(&self, params: &T) -> Result { fn resolve_endpoint(&self, params: &T) -> Result {
self.0.resolve_endpoint(params) self.0.resolve_endpoint(params)

View File

@ -5,7 +5,7 @@
use crate::client::auth::AuthSchemeId; use crate::client::auth::AuthSchemeId;
use crate::client::orchestrator::Future; use crate::client::orchestrator::Future;
use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
use std::any::Any; use std::any::Any;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -23,6 +23,10 @@ pub struct IdentityResolvers {
identity_resolvers: Vec<(AuthSchemeId, Arc<dyn IdentityResolver>)>, identity_resolvers: Vec<(AuthSchemeId, Arc<dyn IdentityResolver>)>,
} }
impl Storable for IdentityResolvers {
type Storer = StoreReplace<IdentityResolvers>;
}
impl IdentityResolvers { impl IdentityResolvers {
pub fn builder() -> builders::IdentityResolversBuilder { pub fn builder() -> builders::IdentityResolversBuilder {
builders::IdentityResolversBuilder::new() builders::IdentityResolversBuilder::new()

View File

@ -9,7 +9,7 @@ pub mod error;
use crate::client::interceptors::context::wrappers::{ use crate::client::interceptors::context::wrappers::{
FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, FinalizerInterceptorContextMut, FinalizerInterceptorContextRef,
}; };
use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend};
use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::error::display::DisplayErrorContext;
pub use context::{ pub use context::{
wrappers::{ wrappers::{
@ -635,6 +635,10 @@ impl Deref for SharedInterceptor {
} }
} }
impl Storable for SharedInterceptor {
type Storer = StoreAppend<SharedInterceptor>;
}
/// Collection of [`SharedInterceptor`] that allows for only registration /// Collection of [`SharedInterceptor`] that allows for only registration
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct InterceptorRegistrar { pub struct InterceptorRegistrar {

View File

@ -5,6 +5,7 @@
//! This module defines types that describe when to retry given a response. //! This module defines types that describe when to retry given a response.
use crate::config_bag::{Storable, StoreReplace};
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
@ -278,6 +279,10 @@ pub struct RetryConfig {
reconnect_mode: ReconnectMode, reconnect_mode: ReconnectMode,
} }
impl Storable for RetryConfig {
type Storer = StoreReplace<RetryConfig>;
}
/// Mode for connection re-establishment /// Mode for connection re-establishment
/// ///
/// By default, when a transient error is encountered, the connection in use will be poisoned. This /// By default, when a transient error is encountered, the connection in use will be poisoned. This

View File

@ -6,6 +6,7 @@
//! This module defines types that describe timeouts that can be applied to various stages of the //! This module defines types that describe timeouts that can be applied to various stages of the
//! Smithy networking stack. //! Smithy networking stack.
use crate::config_bag::{Storable, StoreReplace};
use std::time::Duration; use std::time::Duration;
/// Builder for [`TimeoutConfig`]. /// Builder for [`TimeoutConfig`].
@ -208,6 +209,10 @@ pub struct TimeoutConfig {
operation_attempt_timeout: Option<Duration>, operation_attempt_timeout: Option<Duration>,
} }
impl Storable for TimeoutConfig {
type Storer = StoreReplace<TimeoutConfig>;
}
impl TimeoutConfig { impl TimeoutConfig {
/// Returns a builder to create a `TimeoutConfig`. /// Returns a builder to create a `TimeoutConfig`.
pub fn builder() -> TimeoutConfigBuilder { pub fn builder() -> TimeoutConfigBuilder {

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use std::sync::Mutex; use std::sync::Mutex;
pub(crate) fn uuid_v4(input: u128) -> String { pub(crate) fn uuid_v4(input: u128) -> String {
@ -58,6 +59,10 @@ impl From<&'static str> for IdempotencyTokenProvider {
} }
} }
impl Storable for IdempotencyTokenProvider {
type Storer = StoreReplace<IdempotencyTokenProvider>;
}
impl IdempotencyTokenProvider { impl IdempotencyTokenProvider {
pub fn make_idempotency_token(&self) -> String { pub fn make_idempotency_token(&self) -> String {
match &self.inner { match &self.inner {