diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index f2b8306d8688e3dde2d31cc285d126ef06357b2d..37f3ce94ce5faebe4f427ddecb5852fcb637f07c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -38,6 +38,7 @@ val DECORATORS: List = listOf( HttpConnectorDecorator(), AwsEndpointsStdLib(), AddFIPSDualStackDecorator(), + GenericSmithySdkConfigSettings(), // Service specific decorators ApiGatewayDecorator(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt index a467de9d7bf095a76a72075ea279bfc6c945a89e..02b84bfe057f3f9ca3df0e84b7f31c207e15c1bd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsEndpointDecorator.kt @@ -31,10 +31,13 @@ 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.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.util.extendIf import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rust.codegen.core.util.thenSingletonListOf class AwsEndpointDecorator : ClientCodegenDecorator { override val name: String = "AwsEndpoint" @@ -78,7 +81,7 @@ class AwsEndpointDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations.extendIf(codegenContext.getBuiltIn(Builtins.REGION) != null) { + return baseCustomizations.extendIf(codegenContext.isRegionalized()) { AwsEndpointShimCustomization(codegenContext) } + SdkEndpointCustomization( codegenContext, @@ -101,7 +104,7 @@ class AwsEndpointDecorator : ClientCodegenDecorator { ) } // generate a region converter if params has a region - if (!epTypes.params.toList().any { it.builtIn == Builtins.REGION.builtIn }) { + if (!codegenContext.isRegionalized()) { println("not generating a resolver for ${codegenContext.serviceShape}") return } @@ -127,6 +130,21 @@ class AwsEndpointDecorator : ClientCodegenDecorator { } } + override fun extraSections(codegenContext: ClientCodegenContext): List, (Section) -> Writable>> { + return codegenContext.isRegionalized().thenSingletonListOf { + SdkConfigSection.create { section -> + { + rust( + """ + ${section.serviceConfigBuilder}.set_aws_endpoint_resolver(${section.sdkConfig}.endpoint_resolver().clone()); + ${section.serviceConfigBuilder}.set_endpoint_url(${section.sdkConfig}.endpoint_url().map(|url|url.to_string())); + """, + ) + } + } + } + } + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { return listOf( object : EndpointCustomization { @@ -150,6 +168,7 @@ class AwsEndpointDecorator : ClientCodegenDecorator { "EndpointShim" to endpointShim, "aws_types" to AwsRuntimeType.awsTypes(runtimeConfig), ) + override fun section(section: ServiceConfig) = writable { when (section) { ServiceConfig.BuilderImpl -> rustTemplate( @@ -267,3 +286,5 @@ class AwsEndpointDecorator : ClientCodegenDecorator { } } } + +fun ClientCodegenContext.isRegionalized() = getBuiltIn(Builtins.REGION) != null diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 6891db1eefe1681cc9fd55aa88fc558e064d0da4..001b35b8348c90c617f70cfdb50157cf0619143a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -16,8 +16,10 @@ 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection @@ -46,6 +48,15 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { ): List { return baseCustomizations + PubUseCredentials(codegenContext.runtimeConfig) } + + override fun extraSections(codegenContext: ClientCodegenContext): List, (Section) -> Writable>> = + listOf( + SdkConfigSection.create { section -> + writable { + rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider().cloned());") + } + }, + ) } /** @@ -64,6 +75,7 @@ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomizati """pub(crate) credentials_provider: #{provider}::SharedCredentialsProvider,""", *codegenScope, ) + ServiceConfig.ConfigImpl -> rustTemplate( """ /// Returns the credentials provider. @@ -73,8 +85,10 @@ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomizati """, *codegenScope, ) + ServiceConfig.BuilderStruct -> rustTemplate("credentials_provider: Option<#{provider}::SharedCredentialsProvider>,", *codegenScope) + ServiceConfig.BuilderImpl -> { rustTemplate( """ diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index e77ca75df51d04530fcba8dff1c456bca0a9b2f9..e6a76bd6c6cd89a7086653f44133aab7949f0783 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -21,12 +21,15 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection 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.thenSingletonListOf /* Example Generated Code */ /* @@ -106,6 +109,21 @@ class RegionDecorator : ClientCodegenDecorator { return baseCustomizations.extendIf(usesRegion(codegenContext)) { PubUseRegion(codegenContext.runtimeConfig) } } + override fun extraSections(codegenContext: ClientCodegenContext): List, (Section) -> Writable>> { + return usesRegion(codegenContext).thenSingletonListOf { + SdkConfigSection.create { section -> + { + rust( + """ + ${section.serviceConfigBuilder} = + ${section.serviceConfigBuilder}.region(${section.sdkConfig}.region().cloned()); + """, + ) + } + } + } + } + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { if (!usesRegion(codegenContext)) { return listOf() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 3bf099ed4f30e7168b11d751c11874155c41d463..571a339326351accd0e339a76a7d09e90da94619 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -5,18 +5,64 @@ package software.amazon.smithy.rustsdk -import software.amazon.smithy.rulesengine.language.syntax.parameters.Builtins import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator 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.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.join import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section + +/** + * Section enabling linkage between `SdkConfig` and ::Config + */ +object SdkConfigSection : AdHocSection("SdkConfig") { + /** + * [sdkConfig]: A reference to the SDK config struct + * [serviceConfigBuilder]: A reference (owned) to the `::config::Builder` struct. + * + * Each invocation of this section MUST be a complete statement (ending with a semicolon), e.g: + * ``` + * rust("${section.serviceConfigBuilder}.set_foo(${section.sdkConfig}.foo());") + * ``` + */ + data class CopySdkConfigToClientConfig(val sdkConfig: String, val serviceConfigBuilder: String) : + Section("CopyConfig") +} + +/** + * SdkConfig -> ::Config for settings that come from generic smithy + */ +class GenericSmithySdkConfigSettings : ClientCodegenDecorator { + override val name: String = "GenericSmithySdkConfigSettings" + override val order: Byte = 0 + + override fun extraSections(codegenContext: ClientCodegenContext): List, (Section) -> Writable>> = + listOf( + SdkConfigSection.create { section -> + writable { + rust( + """ + // resiliency + ${section.serviceConfigBuilder}.set_retry_config(${section.sdkConfig}.retry_config().cloned()); + ${section.serviceConfigBuilder}.set_timeout_config(${section.sdkConfig}.timeout_config().cloned()); + ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl()); + + ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned()); + + """, + ) + } + }, + ) +} /** * Adds functionality for constructing `::Config` objects from `aws_types::SdkConfig`s @@ -39,27 +85,15 @@ class SdkConfigDecorator : ClientCodegenDecorator { val codegenScope = arrayOf( "SdkConfig" to AwsRuntimeType.awsTypes(codegenContext.runtimeConfig).resolve("sdk_config::SdkConfig"), ) - val regionalizedBits = writable { - if (codegenContext.getBuiltIn(Builtins.REGION) != null) { - rust("builder = builder.region(input.region().cloned());") - rust("builder.set_aws_endpoint_resolver(input.endpoint_resolver().clone());") - } - } rustCrate.withModule(RustModule.Config) { - // !!NOTE!! As more items are added to aws_types::SdkConfig, use them here to configure the config builder rustTemplate( """ impl From<&#{SdkConfig}> for Builder { fn from(input: &#{SdkConfig}) -> Self { let mut builder = Builder::default(); - #{regionalized} - builder.set_endpoint_url(input.endpoint_url().map(|url|url.to_string())); - builder.set_retry_config(input.retry_config().cloned()); - builder.set_timeout_config(input.timeout_config().cloned()); - builder.set_sleep_impl(input.sleep_impl()); - builder.set_credentials_provider(input.credentials_provider().cloned()); - builder.set_app_name(input.app_name().cloned()); - builder.set_http_connector(input.http_connector().cloned()); + #{augmentBuilder} + + builder } } @@ -70,8 +104,16 @@ class SdkConfigDecorator : ClientCodegenDecorator { } } """, + "augmentBuilder" to codegenContext.rootDecorator.extraSections(codegenContext) + .filter { (t, _) -> t is SdkConfigSection }.map { (_, sectionWriter) -> + sectionWriter( + SdkConfigSection.CopySdkConfigToClientConfig( + sdkConfig = "input", + serviceConfigBuilder = "builder", + ), + ) + }.join("\n"), *codegenScope, - "regionalized" to regionalizedBits, ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index e4e4f579b8f6eaebc0782489bc428d52985abe50..54dc4f5586cb5b0770f5eaf5f89615ca00e76f65 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -16,8 +16,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.util.dq @@ -53,105 +55,123 @@ class UserAgentDecorator : ClientCodegenDecorator { ): List { return baseCustomizations + UserAgentFeature(codegenContext.runtimeConfig) } -} -/** - * Adds a static `API_METADATA` variable to the crate root containing the serviceId & the version of the crate for this individual service - */ -private class ApiVersionAndPubUse(private val runtimeConfig: RuntimeConfig, serviceTrait: ServiceTrait) : - LibRsCustomization() { - private val serviceId = serviceTrait.sdkId.lowercase().replace(" ", "") - override fun section(section: LibRsSection): Writable = when (section) { - is LibRsSection.Body -> writable { - // PKG_VERSION comes from CrateVersionGenerator - rust( - "static API_METADATA: #1T::ApiMetadata = #1T::ApiMetadata::new(${serviceId.dq()}, PKG_VERSION);", - AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), - ) - - // Re-export the app name so that it can be specified in config programmatically without an explicit dependency - rustTemplate("pub use #{AppName};", "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName")) - } - else -> emptySection + override fun extraSections(codegenContext: ClientCodegenContext): List, (Section) -> Writable>> { + return listOf( + SdkConfigSection.create { section -> + writable { rust("${section.serviceConfigBuilder}.set_app_name(${section.sdkConfig}.app_name().cloned());") } + }, + ) } -} -private class UserAgentFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { - override fun section(section: OperationSection): Writable = when (section) { - is OperationSection.MutateRequest -> writable { - rustTemplate( - """ - let mut user_agent = #{ua_module}::AwsUserAgent::new_from_environment( - #{Env}::real(), - crate::API_METADATA.clone(), - ); - if let Some(app_name) = _config.app_name() { - user_agent = user_agent.with_app_name(app_name.clone()); - } - ${section.request}.properties_mut().insert(user_agent); - """, - "ua_module" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), - "Env" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("os_shim_internal::Env"), - ) - } - else -> emptySection - } -} - -private class AppNameCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { - private val codegenScope = arrayOf( - "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), - ) + /** + * Adds a static `API_METADATA` variable to the crate root containing the serviceId & the version of the crate for this individual service + */ + private class ApiVersionAndPubUse(private val runtimeConfig: RuntimeConfig, serviceTrait: ServiceTrait) : + LibRsCustomization() { + private val serviceId = serviceTrait.sdkId.lowercase().replace(" ", "") + override fun section(section: LibRsSection): Writable = when (section) { + is LibRsSection.Body -> writable { + // PKG_VERSION comes from CrateVersionGenerator + rust( + "static API_METADATA: #1T::ApiMetadata = #1T::ApiMetadata::new(${serviceId.dq()}, PKG_VERSION);", + AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), + ) - override fun section(section: ServiceConfig): Writable = - when (section) { - is ServiceConfig.BuilderStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) - } - is ServiceConfig.BuilderImpl -> writable { + // Re-export the app name so that it can be specified in config programmatically without an explicit dependency rustTemplate( - """ - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn app_name(mut self, app_name: #{AppName}) -> Self { - self.set_app_name(Some(app_name)); - self - } - - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn set_app_name(&mut self, app_name: Option<#{AppName}>) -> &mut Self { - self.app_name = app_name; - self - } - """, - *codegenScope, + "pub use #{AppName};", + "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), ) } - is ServiceConfig.BuilderBuild -> writable { - rust("app_name: self.app_name,") - } - is ServiceConfig.ConfigStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) - } - is ServiceConfig.ConfigImpl -> writable { + + else -> emptySection + } + } + + private class UserAgentFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { + override fun section(section: OperationSection): Writable = when (section) { + is OperationSection.MutateRequest -> writable { 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() + let mut user_agent = #{ua_module}::AwsUserAgent::new_from_environment( + #{Env}::real(), + crate::API_METADATA.clone(), + ); + if let Some(app_name) = _config.app_name() { + user_agent = user_agent.with_app_name(app_name.clone()); } + ${section.request}.properties_mut().insert(user_agent); """, - *codegenScope, + "ua_module" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent"), + "Env" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("os_shim_internal::Env"), ) } + else -> emptySection } + } + + private class AppNameCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { + private val codegenScope = arrayOf( + "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), + ) + + override fun section(section: ServiceConfig): Writable = + when (section) { + is ServiceConfig.BuilderStruct -> writable { + rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + } + + is ServiceConfig.BuilderImpl -> writable { + rustTemplate( + """ + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn app_name(mut self, app_name: #{AppName}) -> Self { + self.set_app_name(Some(app_name)); + self + } + + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_app_name(&mut self, app_name: Option<#{AppName}>) -> &mut Self { + self.app_name = app_name; + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderBuild -> writable { + rust("app_name: self.app_name,") + } + + is ServiceConfig.ConfigStruct -> writable { + rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + } + + is ServiceConfig.ConfigImpl -> writable { + 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 + } + } } diff --git a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs index 26e8b0142be994a5025a68a8f20f727631bb0580..25e5b0c404606b6e2d3e459fd5eb5b8515ed869c 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs @@ -19,7 +19,8 @@ async fn endpoints_can_be_overridden_globally() { .credentials_provider(Credentials::new("asdf", "asdf", None, None, "test")) .build(); let svc = aws_sdk_dynamodb::Client::from_conf(conf); - let _ = svc.list_tables().send().await; + // dbg to see why the request failed if this test is failing + let _ = dbg!(svc.list_tables().send().await); assert_eq!( request.expect_request().uri(), &Uri::from_static("http://localhost:8000") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index cd15fa7d30ef3cf87ff0450fae12dc830f2fede4..6a33d82c96054dea40fc787dc368360dd786d92f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.customize import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations @@ -61,6 +62,11 @@ interface CoreCodegenDecorator { codegenContext: CodegenContext, baseCustomizations: List, ): List = baseCustomizations + + /** + * Extra sections allow one decorator to influence another. This is intended to be used by querying the `rootDecorator` + */ + fun extraSections(codegenContext: CodegenContext): List, (Section) -> Writable>> = listOf() } /** @@ -93,6 +99,9 @@ abstract class CombinedCoreCodegenDecorator, (Section) -> Writable>> = + addCustomizations { decorator -> decorator.extraSections(codegenContext) } + /** * Combines customizations from multiple ordered codegen decorators. * diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/NamedSectionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/NamedSectionGenerator.kt index 516bfc4284eb38f362c8e56a25b2e1e9a868dfe6..ccc9702e09a2a034a6007d98b2fc90707c820849 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/NamedSectionGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/NamedSectionGenerator.kt @@ -23,6 +23,19 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable */ abstract class Section(val name: String) +/** + * Detatched section abstraction to allow adhoc sections to be created. By using the `.writer` method, a instantiation + * of this section can be easily created. + */ +abstract class AdHocSection(val name: String) { + /** + * Helper to enable easily combining detached sections with the [CoreCodegenDecorator.extraSections] method. + */ + fun create(w: (T) -> Writable): Pair, (Section) -> Writable> = this to { s: Section -> + w((s as T)) + } +} + /** * A named section generator allows customization via a predefined set of named sections. * diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt index b94b81fad00c5dca8b50e33ac90e07181b998355..c18cc56f746c7419c7b76b1fd2152e3769629012 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/LetIf.kt @@ -18,3 +18,9 @@ fun List.extendIf(condition: Boolean, f: () -> T) = if (condition) { } else { this } + +fun Boolean.thenSingletonListOf(f: () -> T): List = if (this) { + listOf(f()) +} else { + listOf() +}