Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changelog/hints-mostly-unused.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
applies_to:
- aws-sdk-rust
- client
authors:
- landonxjames
references:
- smithy-rs#4208
breaking: false
new_feature: true
bug_fix: false
---
Add the ability to add `hints.mostly-unused = true` to Cargo.toml. Also enable this hint for the `aws-sdk-ec2`, `aws-sdk-s3`, and `aws-sdk-dynamodb` crates.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package software.amazon.smithy.rustsdk

import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMetadataDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMetadataSettings
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ManifestHintsDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customizations.ManifestHintsSettings
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator
import software.amazon.smithy.rustsdk.customize.AwsDisableStalledStreamProtection
Expand Down Expand Up @@ -72,6 +74,11 @@ val DECORATORS: List<ClientCodegenDecorator> =
Sigv4aAuthTraitBackfillDecorator(),
EndpointBasedAuthSchemeDecorator(),
SpanDecorator(),
ManifestHintsDecorator(
ManifestHintsSettings(
mostlyUnused = true,
),
),
// TODO(https://github.com/smithy-lang/smithy-rs/issues/3863): Comment in once the issue has been resolved
// SmokeTestsDecorator(),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): List<ClientCodegenDec
},
)

/** Only apply this decorator to the given list of service IDs */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't end up needing this after I moved the service list to gradle.properties, but seemed like it could be a useful utility in the future, so I left it.

fun ClientCodegenDecorator.onlyApplyToList(serviceIds: List<String>): List<ClientCodegenDecorator> =
serviceIds.map {
ConditionalDecorator(this) { _, serviceShapeId ->
serviceShapeId == ShapeId.from(it)
}
}

/** Apply this decorator to all services but the one identified by ID */
fun ClientCodegenDecorator.applyExceptFor(serviceId: String): List<ClientCodegenDecorator> =
listOf(
Expand Down
14 changes: 12 additions & 2 deletions aws/sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ fun loadServiceMembership(): Membership {
return membershipOverride ?: fullSdk
}

val hintMostlyUnusedList: Set<String> by lazy { hintMostlyUnusedList() }

fun hintMostlyUnusedList(): Set<String> {
val list = properties.get("aws.services.hints.mostlyUnused") ?: ""
return list.split(",").map { it.trim() }.toSet()
}

fun generateSmithyBuild(services: AwsServices): String {
val awsConfigVersion = properties.get(CrateSet.STABLE_VERSION_PROP_NAME)
?: throw IllegalStateException("missing ${CrateSet.STABLE_VERSION_PROP_NAME} for aws-config version")
Expand All @@ -92,6 +99,7 @@ fun generateSmithyBuild(services: AwsServices): String {
services.partitionsConfigPath.let { software.amazon.smithy.utils.StringUtils.escapeJavaString(it, "") }
val integrationTestPath = project.projectDir.resolve("integration-tests")
.let { software.amazon.smithy.utils.StringUtils.escapeJavaString(it, "") }
val hintMostlyUnusedListMembers = hintMostlyUnusedList.joinToString(", ") { "\"$it\"" }
"""
"${service.module}": {
"imports": [${files.joinToString()}],
Expand All @@ -118,6 +126,7 @@ fun generateSmithyBuild(services: AwsServices): String {
"moduleRepository": "https://github.com/awslabs/aws-sdk-rust",
"license": "Apache-2.0",
"minimumSupportedRustVersion": "${getRustMSRV()}",
"hintMostlyUnusedList": [$hintMostlyUnusedListMembers],
"customizationConfig": {
"awsSdk": {
"awsSdkBuild": true,
Expand Down Expand Up @@ -148,6 +157,7 @@ fun generateSmithyBuild(services: AwsServices): String {
tasks.register("generateSmithyBuild") {
description = "generate smithy-build.json"
inputs.property("servicelist", awsServices.services.toString())
inputs.property("hintMostlyUnusedList", hintMostlyUnusedList)
inputs.dir(projectDir.resolve("aws-models"))
outputs.file(layout.buildDirectory.file("smithy-build.json"))

Expand Down Expand Up @@ -494,8 +504,8 @@ fun Project.registerDowngradeFor(
val crateNameToLastKnownWorkingVersions =
mapOf(
"minicbor" to "0.24.2",
"libfuzzer-sys" to "0.4.7" // TODO(https://github.com/rust-fuzz/libfuzzer/issues/126)
)
"libfuzzer-sys" to "0.4.7", // TODO(https://github.com/rust-fuzz/libfuzzer/issues/126)
)

crateNameToLastKnownWorkingVersions.forEach { (crate, version) ->
// doesn't matter even if the specified crate does not exist in the lockfile
Expand Down
9 changes: 8 additions & 1 deletion aws/sdk/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
#

aws.services=
aws.services.hints.mostlyUnused=\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably want to remove this.

aws-sd-cloudformation,\
aws-sdk-dynamodb,\
aws-sdk-ec2,\
aws-sdk-s3,\
aws-sdk-sns,\
aws-sdk-sqs,\
aws-sdk-ssm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ data class ClientRustSettings(
override val examplesUri: String?,
override val minimumSupportedRustVersion: String? = null,
override val customizationConfig: ObjectNode?,
override val hintMostlyUnusedList: List<String>,
) : CoreRustSettings(
service,
moduleName,
Expand All @@ -51,6 +52,7 @@ data class ClientRustSettings(
examplesUri,
minimumSupportedRustVersion,
customizationConfig,
hintMostlyUnusedList,
) {
companion object {
fun from(
Expand All @@ -73,6 +75,7 @@ data class ClientRustSettings(
examplesUri = coreRustSettings.examplesUri,
minimumSupportedRustVersion = coreRustSettings.minimumSupportedRustVersion,
customizationConfig = coreRustSettings.customizationConfig,
hintMostlyUnusedList = coreRustSettings.hintMostlyUnusedList,
)
}
}
Expand Down Expand Up @@ -115,14 +118,29 @@ data class ClientCodegenConfig(
) = if (node.isPresent) {
ClientCodegenConfig(
formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds,
flattenCollectionAccessors = node.get().getBooleanMemberOrDefault("flattenCollectionAccessors", DEFAULT_FLATTEN_ACCESSORS),
flattenCollectionAccessors =
node.get()
.getBooleanMemberOrDefault("flattenCollectionAccessors", DEFAULT_FLATTEN_ACCESSORS),
debugMode = coreCodegenConfig.debugMode,
renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", DEFAULT_RENAME_EXCEPTIONS),
includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", DEFAULT_INCLUDE_FLUENT_CLIENT),
addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", DEFAULT_ADD_MESSAGE_TO_ERRORS),
includeEndpointUrlConfig = node.get().getBooleanMemberOrDefault("includeEndpointUrlConfig", DEFAULT_INCLUDE_ENDPOINT_URL_CONFIG),
enableUserConfigurableRuntimePlugins = node.get().getBooleanMemberOrDefault("enableUserConfigurableRuntimePlugins", DEFAULT_ENABLE_USER_CONFIGURABLE_RUNTIME_PLUGINS),
nullabilityCheckMode = NullableIndex.CheckMode.valueOf(node.get().getStringMemberOrDefault("nullabilityCheckMode", DEFAULT_NULLABILITY_CHECK_MODE)),
includeFluentClient =
node.get()
.getBooleanMemberOrDefault("includeFluentClient", DEFAULT_INCLUDE_FLUENT_CLIENT),
addMessageToErrors =
node.get()
.getBooleanMemberOrDefault("addMessageToErrors", DEFAULT_ADD_MESSAGE_TO_ERRORS),
includeEndpointUrlConfig =
node.get()
.getBooleanMemberOrDefault("includeEndpointUrlConfig", DEFAULT_INCLUDE_ENDPOINT_URL_CONFIG),
enableUserConfigurableRuntimePlugins =
node.get().getBooleanMemberOrDefault(
"enableUserConfigurableRuntimePlugins",
DEFAULT_ENABLE_USER_CONFIGURABLE_RUNTIME_PLUGINS,
),
nullabilityCheckMode =
NullableIndex.CheckMode.valueOf(
node.get().getStringMemberOrDefault("nullabilityCheckMode", DEFAULT_NULLABILITY_CHECK_MODE),
),
)
} else {
ClientCodegenConfig(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.customize.ClientCodegenDecorator
import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations

/**
* See https://blog.rust-lang.org/inside-rust/2025/07/15/call-for-testing-hint-mostly-unused/ for more information
*/
data class ManifestHintsSettings(
val mostlyUnused: Boolean? = null,
)

fun ManifestHintsSettings.asMap(): Map<String, Any> {
val inner =
listOfNotNull(
mostlyUnused?.let { "mostly-unused" to it },
).toMap()
return mapOf("hints" to inner)
}

/**
* Write compilation hints metdata settings into Cargo.toml
*
* # Notes
* This decorator is not used by default, code generators must manually configure and include it in their builds.
*/
class ManifestHintsDecorator(private val manifestHintsSettings: ManifestHintsSettings) : ClientCodegenDecorator {
override val name: String = "ManifestHintsDecorator"
override val order: Byte = 0

override fun crateManifestCustomizations(codegenContext: ClientCodegenContext): ManifestCustomizations {
if (codegenContext.settings.hintMostlyUnusedList.contains(codegenContext.settings.moduleName)) {
return manifestHintsSettings.asMap()
} else {
return emptyMap()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ fun testClientRustSettings(
examplesUri: String? = null,
minimumSupportedRustVersion: String? = null,
customizationConfig: ObjectNode? = null,
hintMostlyUnusedList: List<String> = emptyList(),
) = ClientRustSettings(
service,
moduleName,
Expand All @@ -51,6 +52,7 @@ fun testClientRustSettings(
examplesUri,
minimumSupportedRustVersion,
customizationConfig,
hintMostlyUnusedList,
)

val TestClientRustSymbolProviderConfig =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.rust.codegen.core.util.orNull
import java.util.Optional
import java.util.logging.Logger
import kotlin.jvm.optionals.getOrElse
import kotlin.streams.toList

private const val SERVICE = "service"
Expand All @@ -29,6 +30,7 @@ private const val EXAMPLES = "examples"
private const val MINIMUM_SUPPORTED_RUST_VERSION = "minimumSupportedRustVersion"
private const val CUSTOMIZATION_CONFIG = "customizationConfig"
const val CODEGEN_SETTINGS = "codegen"
private const val HINT_MOSTLY_UNUSED_LIST = "hintMostlyUnusedList"

/**
* [CoreCodegenConfig] contains code-generation configuration that is _common to all_ smithy-rs plugins.
Expand All @@ -52,9 +54,13 @@ open class CoreCodegenConfig(
fun fromNode(node: Optional<ObjectNode>): CoreCodegenConfig =
if (node.isPresent) {
CoreCodegenConfig(
formatTimeoutSeconds = node.get().getNumberMemberOrDefault("formatTimeoutSeconds", DEFAULT_FORMAT_TIMEOUT_SECONDS).toInt(),
formatTimeoutSeconds =
node.get()
.getNumberMemberOrDefault("formatTimeoutSeconds", DEFAULT_FORMAT_TIMEOUT_SECONDS).toInt(),
debugMode = node.get().getBooleanMemberOrDefault("debugMode", DEFAULT_DEBUG_MODE),
flattenCollectionAccessors = node.get().getBooleanMemberOrDefault("flattenCollectionAccessors", DEFAULT_FLATTEN_MODE),
flattenCollectionAccessors =
node.get()
.getBooleanMemberOrDefault("flattenCollectionAccessors", DEFAULT_FLATTEN_MODE),
)
} else {
CoreCodegenConfig(
Expand Down Expand Up @@ -91,6 +97,7 @@ open class CoreRustSettings(
open val examplesUri: String? = null,
open val minimumSupportedRustVersion: String? = null,
open val customizationConfig: ObjectNode? = null,
open val hintMostlyUnusedList: List<String> = emptyList(),
) {
/**
* Get the corresponding [ServiceShape] from a model.
Expand Down Expand Up @@ -123,12 +130,14 @@ open class CoreRustSettings(
"contain any service shapes",
)
}

services.size > 1 -> {
throw CodegenException(
"Cannot infer service to generate because the model contains " +
"multiple service shapes: " + services,
)
}

else -> {
val service = services[0]
LOGGER.info("Inferring service to generate as: $service")
Expand Down Expand Up @@ -180,6 +189,7 @@ open class CoreRustSettings(
LICENSE,
MINIMUM_SUPPORTED_RUST_VERSION,
CUSTOMIZATION_CONFIG,
HINT_MOSTLY_UNUSED_LIST,
),
)

Expand All @@ -202,6 +212,14 @@ open class CoreRustSettings(
examplesUri = config.getStringMember(EXAMPLES).orNull()?.value,
minimumSupportedRustVersion = config.getStringMember(MINIMUM_SUPPORTED_RUST_VERSION).orNull()?.value,
customizationConfig = config.getObjectMember(CUSTOMIZATION_CONFIG).orNull(),
hintMostlyUnusedList =
config.getArrayMember("hintMostlyUnusedList").map {
it.elements.mapNotNull { node ->
node.asStringNode().map { stringNode ->
stringNode.value
}.orNull()
}
}.getOrElse { emptyList() },
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -83,8 +82,15 @@ class TsServerCodegenVisitor(
publicConstrainedTypes: Boolean,
includeConstraintShapeProvider: Boolean,
codegenDecorator: ServerCodegenDecorator,
) =
RustServerCodegenTsPlugin.baseSymbolProvider(settings, model, serviceShape, rustSymbolProviderConfig, publicConstrainedTypes, includeConstraintShapeProvider, codegenDecorator)
) = RustServerCodegenTsPlugin.baseSymbolProvider(
settings,
model,
serviceShape,
rustSymbolProviderConfig,
publicConstrainedTypes,
includeConstraintShapeProvider,
codegenDecorator,
)

val serverSymbolProviders =
ServerSymbolProviders.from(
Expand Down Expand Up @@ -171,7 +177,7 @@ class TsServerCodegenVisitor(
fun tsServerEnumGeneratorFactory(
codegenContext: ServerCodegenContext,
shape: StringShape,
) = TsServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator)
) = TsServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator, emptyList())
stringShape(shape, ::tsServerEnumGeneratorFactory)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Attribute
import software.amazon.smithy.rust.codegen.core.rustlang.Writable
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.writable
import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumCustomization
import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator
import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext
Expand Down Expand Up @@ -41,6 +42,7 @@ class TsServerEnumGenerator(
codegenContext: ServerCodegenContext,
shape: StringShape,
validationExceptionConversionGenerator: ValidationExceptionConversionGenerator,
customizations: List<EnumCustomization>,
) : EnumGenerator(
codegenContext.model,
codegenContext.symbolProvider,
Expand All @@ -50,4 +52,5 @@ class TsServerEnumGenerator(
shape,
validationExceptionConversionGenerator,
),
customizations,
)
Loading