Skip to content

Commit 27ca7f1

Browse files
Merge branch 'main' into fahadzub/cbor-constraint
2 parents 35a9099 + 684c15f commit 27ca7f1

File tree

8 files changed

+207
-22
lines changed

8 files changed

+207
-22
lines changed

aws/rust-runtime/aws-config/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aws/rust-runtime/aws-config/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-config"
3-
version = "1.5.7"
3+
version = "1.5.8"
44
authors = [
55
"AWS Rust SDK Team <[email protected]>",
66
"Russell Cohen <[email protected]>",

aws/rust-runtime/aws-config/src/web_identity_token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! WebIdentityTokenCredentialProvider will load the following environment variables:
2020
//! - `AWS_WEB_IDENTITY_TOKEN_FILE`: **required**, location to find the token file containing a JWT token
2121
//! - `AWS_ROLE_ARN`: **required**, role ARN to assume
22-
//! - `AWS_IAM_ROLE_SESSION_NAME`: **optional**: Session name to use when assuming the role
22+
//! - `AWS_ROLE_SESSION_NAME`: **optional**: Session name to use when assuming the role
2323
//!
2424
//! ## AWS Profile Configuration
2525
//! _Note: Configuration of the web identity token provider via a shared profile is only supported

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ import software.amazon.smithy.rust.codegen.core.util.letIf
4040
import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty
4141
import software.amazon.smithy.rust.codegen.core.util.runCommand
4242
import java.io.File
43+
import java.io.FileInputStream
4344
import java.nio.file.Files
4445
import java.nio.file.Files.createTempDirectory
4546
import java.nio.file.Path
47+
import java.util.Properties
4648
import kotlin.io.path.absolutePathString
4749
import kotlin.io.path.writeText
4850

@@ -54,6 +56,8 @@ val TestModuleDocProvider =
5456
}
5557
}
5658

59+
val projectRootDir by lazy { File("git rev-parse --show-toplevel".runCommand().replace("\n", "")) }
60+
5761
/**
5862
* Waiting for Kotlin to stabilize their temp directory functionality
5963
*/
@@ -65,6 +69,38 @@ private fun tempDir(directory: File? = null): File {
6569
}
6670
}
6771

72+
/**
73+
* This function returns the minimum supported Rust version, as specified in the `gradle.properties` file
74+
* located at the root of the project.
75+
*/
76+
fun msrv(): String {
77+
val properties = Properties()
78+
val propertiesFilePath = projectRootDir.resolve("gradle.properties")
79+
80+
FileInputStream(propertiesFilePath).use { inputStream ->
81+
properties.load(inputStream)
82+
}
83+
84+
return properties.getProperty("rust.msrv")
85+
}
86+
87+
/**
88+
* Generates the `rust-toolchain.toml` file in the specified directory.
89+
*
90+
* The compiler version is set in `gradle.properties` under the `rust.msrv` property.
91+
* The Gradle task `GenerateMsrvTask` generates the Kotlin class
92+
* `software.amazon.smithy.rust.codegen.core.Msrv` and writes the value of `rust.msrv` into it.
93+
*/
94+
private fun File.generateRustToolchainToml() {
95+
resolve("rust-toolchain.toml").writeText(
96+
// Help rust select the right version when we run cargo test.
97+
"""
98+
[toolchain]
99+
channel = "${msrv()}"
100+
""".trimIndent(),
101+
)
102+
}
103+
68104
/**
69105
* Creates a Cargo workspace shared among all tests
70106
*
@@ -87,8 +123,7 @@ object TestWorkspace {
87123
private val subprojects = mutableListOf<String>()
88124

89125
private val cargoLock: File by lazy {
90-
val projectDir = "git rev-parse --show-toplevel".runCommand().replace("\n", "")
91-
File(projectDir).resolve("aws/sdk/Cargo.lock")
126+
projectRootDir.resolve("aws/sdk/Cargo.lock")
92127
}
93128

94129
init {
@@ -121,12 +156,7 @@ object TestWorkspace {
121156
version = "0.0.1"
122157
""".trimIndent(),
123158
)
124-
newProject.resolve("rust-toolchain.toml").writeText(
125-
// help rust select the right version when we run cargo test
126-
// TODO(https://github.com/smithy-lang/smithy-rs/issues/2048): load this from the msrv property using a
127-
// method as we do for runtime crate versions
128-
"[toolchain]\nchannel = \"1.78.0\"\n",
129-
)
159+
newProject.generateRustToolchainToml()
130160
// ensure there at least an empty lib.rs file to avoid broken crates
131161
newProject.resolve("src").mkdirs()
132162
newProject.resolve("src/lib.rs").writeText("")
@@ -181,7 +211,11 @@ fun generatePluginContext(
181211
runtimeConfig: RuntimeConfig? = null,
182212
overrideTestDir: File? = null,
183213
): Pair<PluginContext, Path> {
184-
val testDir = overrideTestDir ?: TestWorkspace.subproject()
214+
val testDir =
215+
overrideTestDir?.apply {
216+
mkdirs()
217+
generateRustToolchainToml()
218+
} ?: TestWorkspace.subproject()
185219
val moduleName = "test_${testDir.nameWithoutExtension}"
186220
val testPath = testDir.toPath()
187221
val manifest = FileManifest.create(testPath)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.rust.codegen.core.util
7+
8+
import io.kotest.matchers.booleans.shouldBeTrue
9+
import io.kotest.matchers.paths.shouldExist
10+
import io.kotest.matchers.shouldNotBe
11+
import org.junit.jupiter.api.Test
12+
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
13+
import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext
14+
import software.amazon.smithy.rust.codegen.core.testutil.projectRootDir
15+
import java.nio.file.Files.createTempDirectory
16+
import java.util.regex.Pattern
17+
18+
internal class RustToolChainTomlTest {
19+
val model =
20+
"""
21+
namespace test
22+
23+
service TestService {
24+
version: "123",
25+
operations: [TestOperation]
26+
}
27+
28+
operation TestOperation {
29+
input:= {}
30+
output:= {}
31+
}
32+
""".asSmithyModel(smithyVersion = "2")
33+
34+
@Test
35+
fun `override test directory in integration test has a rust-toolchain toml file`() {
36+
val dir = createTempDirectory("smithy-test").toFile()
37+
val (_, path) = generatePluginContext(model, overrideTestDir = dir)
38+
path.shouldExist()
39+
val rustToolchainTomlPath = path.resolve("rust-toolchain.toml")
40+
rustToolchainTomlPath.shouldExist()
41+
}
42+
43+
@Test
44+
fun `rust-toolchain toml file has correct value from gradle properties for rust-msrv`() {
45+
val (_, path) = generatePluginContext(model)
46+
val rustToolchainTomlPath = path.resolve("rust-toolchain.toml")
47+
rustToolchainTomlPath.shouldExist()
48+
49+
// Read the MSRV written in `gradle.properties` file.
50+
val msrvPattern = Pattern.compile("rust\\.msrv=(.+)")
51+
val gradlePropertiesPath = projectRootDir.resolve("gradle.properties")
52+
val msrv =
53+
gradlePropertiesPath.useLines { lines ->
54+
lines.firstNotNullOfOrNull { line ->
55+
msrvPattern.matcher(line).let { matcher ->
56+
if (matcher.find()) matcher.group(1) else null
57+
}
58+
}
59+
}
60+
msrv shouldNotBe null
61+
62+
// Read `channel = (\d+)` from `rust-toolchain.toml` file, and
63+
// ensure it matches the one in `gradle.properties`.
64+
val toolchainPattern = Pattern.compile("\\[toolchain]")
65+
val channelPattern = Pattern.compile("channel\\s*=\\s*\"(.+)\"")
66+
67+
val channelMatches =
68+
rustToolchainTomlPath.toFile().useLines { lines ->
69+
// Skip lines until the [toolchain] table is found, then take all lines until the next table.
70+
val toolchainSection =
71+
lines
72+
.dropWhile { !toolchainPattern.matcher(it).find() }
73+
.drop(1)
74+
.takeWhile { !it.trim().startsWith("[") }
75+
76+
// There should be a [toolchain] table, and it must have a key called 'channel' whose value must
77+
// match the `rust.msrv` specified in gradle.properties.
78+
toolchainSection != null &&
79+
toolchainSection.any { line ->
80+
channelPattern.matcher(line).let { matcher ->
81+
matcher.find() && matcher.group(1) == msrv
82+
}
83+
}
84+
}
85+
86+
channelMatches.shouldBeTrue()
87+
}
88+
}

codegen-serde/src/main/kotlin/software/amazon/smithy/rust/codegen/serde/SerializeImplGenerator.kt

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import software.amazon.smithy.model.shapes.BlobShape
1010
import software.amazon.smithy.model.shapes.BooleanShape
1111
import software.amazon.smithy.model.shapes.CollectionShape
1212
import software.amazon.smithy.model.shapes.DocumentShape
13+
import software.amazon.smithy.model.shapes.DoubleShape
14+
import software.amazon.smithy.model.shapes.FloatShape
1315
import software.amazon.smithy.model.shapes.MapShape
1416
import software.amazon.smithy.model.shapes.MemberShape
1517
import software.amazon.smithy.model.shapes.NumberShape
@@ -208,13 +210,45 @@ class SerializeImplGenerator(private val codegenContext: CodegenContext) {
208210
* For enums, it adds `as_str()` to convert it into a string directly.
209211
*/
210212
private fun serializeNumber(shape: NumberShape): RuntimeType {
213+
val numericType = SimpleShapes.getValue(shape::class)
214+
return when (shape) {
215+
is FloatShape, is DoubleShape -> serializeFloat(shape)
216+
else ->
217+
RuntimeType.forInlineFun(
218+
numericType.toString(),
219+
PrimitiveShapesModule,
220+
) {
221+
implSerializeConfigured(symbolBuilder(shape, numericType).build()) {
222+
rustTemplate("self.value.serialize(serializer)")
223+
}
224+
}
225+
}
226+
}
227+
228+
private fun serializeFloat(shape: NumberShape): RuntimeType {
211229
val numericType = SimpleShapes.getValue(shape::class)
212230
return RuntimeType.forInlineFun(
213231
numericType.toString(),
214232
PrimitiveShapesModule,
215233
) {
216234
implSerializeConfigured(symbolBuilder(shape, numericType).build()) {
217-
rustTemplate("self.value.serialize(serializer)")
235+
rustTemplate(
236+
"""
237+
if !self.settings.out_of_range_floats_as_strings {
238+
return self.value.serialize(serializer)
239+
}
240+
if self.value.is_nan() {
241+
serializer.serialize_str("NaN")
242+
} else if *self.value == #{ty}::INFINITY {
243+
serializer.serialize_str("Infinity")
244+
} else if *self.value == #{ty}::NEG_INFINITY {
245+
serializer.serialize_str("-Infinity")
246+
} else {
247+
self.value.serialize(serializer)
248+
}
249+
""",
250+
"ty" to numericType,
251+
)
218252
}
219253
}
220254
}

codegen-serde/src/main/kotlin/software/amazon/smithy/rust/codegen/serde/SupportStructures.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ object SupportStructures {
4747
{
4848
use #{serde}::Serialize;
4949
value
50-
.serialize_ref(&#{SerializationSettings} { redact_sensitive_fields: true })
50+
.serialize_ref(&#{SerializationSettings}::redact_sensitive_fields())
5151
.serialize(serializer)
5252
}
5353
""",
@@ -70,7 +70,7 @@ object SupportStructures {
7070
{
7171
use #{serde}::Serialize;
7272
value
73-
.serialize_ref(&#{SerializationSettings} { redact_sensitive_fields: false })
73+
.serialize_ref(&#{SerializationSettings}::leak_sensitive_fields())
7474
.serialize(serializer)
7575
}
7676
""",
@@ -211,7 +211,6 @@ object SupportStructures {
211211

212212
private fun serializationSettings() =
213213
RuntimeType.forInlineFun("SerializationSettings", supportModule) {
214-
// TODO(serde): Consider removing `derive(Default)`
215214
rustTemplate(
216215
"""
217216
/// Settings for use when serializing structures
@@ -220,17 +219,23 @@ object SupportStructures {
220219
pub struct SerializationSettings {
221220
/// Replace all sensitive fields with `<redacted>` during serialization
222221
pub redact_sensitive_fields: bool,
222+
223+
/// Serialize Nan, infinity and negative infinity as strings.
224+
///
225+
/// For protocols like JSON, this avoids the loss-of-information that occurs when these out-of-range values
226+
/// are serialized as null.
227+
pub out_of_range_floats_as_strings: bool,
223228
}
224229
225230
impl SerializationSettings {
226231
/// Replace all `@sensitive` fields with `<redacted>` when serializing.
227232
///
228233
/// Note: This may alter the type of the serialized output and make it impossible to deserialize as
229234
/// numerical fields will be replaced with strings.
230-
pub const fn redact_sensitive_fields() -> Self { Self { redact_sensitive_fields: true } }
235+
pub const fn redact_sensitive_fields() -> Self { Self { redact_sensitive_fields: true, out_of_range_floats_as_strings: false } }
231236
232237
/// Preserve the contents of sensitive fields during serializing
233-
pub const fn leak_sensitive_fields() -> Self { Self { redact_sensitive_fields: false } }
238+
pub const fn leak_sensitive_fields() -> Self { Self { redact_sensitive_fields: false, out_of_range_floats_as_strings: false } }
234239
}
235240
""",
236241
)

0 commit comments

Comments
 (0)