diff --git a/.changelog/1763738215.md b/.changelog/1763738215.md
new file mode 100644
index 00000000000..8aed807df6a
--- /dev/null
+++ b/.changelog/1763738215.md
@@ -0,0 +1,13 @@
+---
+applies_to:
+- client
+- aws-sdk-rust
+authors:
+- AmitKulkarni23
+references:
+- smithy-rs#312
+breaking: false
+new_feature: true
+bug_fix: false
+---
+Add support for Smithy bigInteger and bigDecimal types as string wrappers in aws-smithy-types, allowing users to parse with their preferred big number library.
diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts
index 30997b3b1b0..9f919b0cbdd 100644
--- a/codegen-client-test/build.gradle.kts
+++ b/codegen-client-test/build.gradle.kts
@@ -62,6 +62,7 @@ data class ClientTest(
val allCodegenTests = listOf(
ClientTest("com.amazonaws.simple#SimpleService", "simple", dependsOn = listOf("simple.smithy")),
+ ClientTest("com.amazonaws.bignumbers#BigNumberService", "big_numbers", dependsOn = listOf("big-numbers.smithy")),
ClientTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"),
ClientTest("com.amazonaws.ebs#Ebs", "ebs", dependsOn = listOf("ebs.json")),
ClientTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"),
diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/BigNumberPrecisionTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/BigNumberPrecisionTest.kt
new file mode 100644
index 00000000000..d50855f5090
--- /dev/null
+++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/BigNumberPrecisionTest.kt
@@ -0,0 +1,244 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.rust.codegen.client.smithy
+
+import org.junit.jupiter.api.Test
+import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
+import software.amazon.smithy.rust.codegen.core.rustlang.rawRust
+import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
+import software.amazon.smithy.rust.codegen.core.testutil.unitTest
+
+class BigNumberPrecisionTest {
+ @Test
+ fun `test BigInteger and BigDecimal round trip through serializers with restJson1`() {
+ val model =
+ """
+ namespace test
+ use aws.protocols#restJson1
+
+ @restJson1
+ service TestService {
+ version: "2026-01-01",
+ operations: [TestOp]
+ }
+
+ @http(uri: "/test", method: "POST")
+ operation TestOp {
+ input: TestInput,
+ output: TestOutput
+ }
+
+ structure TestInput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+
+ structure TestOutput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+ """.asSmithyModel()
+
+ clientIntegrationTest(model) { _, rustCrate ->
+ rustCrate.unitTest("big_number_round_trip") {
+ rawRust(
+ """
+ use aws_smithy_types::{BigInteger, BigDecimal};
+ use std::str::FromStr;
+
+ // Test values that exceed native type limits
+ let big_int_str = "99999999999999999999999999"; // > u64::MAX
+ let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision (15-17 digits)
+ let big_dec_magnitude_str = "1.8e308"; // > f64::MAX - tokenizer uses NaN for validation
+
+ // Test 1: High precision BigDecimal
+ let input = crate::operation::test_op::TestOpInput::builder()
+ .big_int(BigInteger::from_str(big_int_str).unwrap())
+ .big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
+ .build()
+ .unwrap();
+
+ let json_body = crate::protocol_serde::shape_test_op::ser_test_op_input(&input).unwrap();
+ let serialized = String::from_utf8(json_body.bytes().unwrap().to_vec()).unwrap();
+
+ assert!(serialized.contains(big_int_str));
+ assert!(serialized.contains(big_dec_precision_str));
+
+ // Test 2: Large magnitude BigDecimal (> f64::MAX)
+ let mut json_response = String::from(r#"{"bigInt":"#);
+ json_response.push_str(big_int_str);
+ json_response.push_str(r#","bigDec":"#);
+ json_response.push_str(big_dec_magnitude_str);
+ json_response.push('}');
+
+ let headers = ::aws_smithy_runtime_api::http::Headers::new();
+ let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
+ 200,
+ &headers,
+ json_response.as_bytes()
+ ).unwrap();
+
+ assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
+ assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
+ """,
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `test BigInteger and BigDecimal round trip through serializers with restXml`() {
+ val model =
+ """
+ namespace test
+ use aws.protocols#restXml
+
+ @restXml
+ service TestService {
+ version: "2026-01-01",
+ operations: [TestOp]
+ }
+
+ @http(uri: "/test", method: "POST")
+ operation TestOp {
+ input: TestInput,
+ output: TestOutput
+ }
+
+ structure TestInput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+
+ structure TestOutput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+ """.asSmithyModel()
+
+ clientIntegrationTest(model) { _, rustCrate ->
+ rustCrate.unitTest("big_number_round_trip_xml") {
+ rawRust(
+ """
+ use aws_smithy_types::{BigInteger, BigDecimal};
+ use std::str::FromStr;
+
+ // Test values that exceed native type limits
+ let big_int_str = "99999999999999999999999999"; // > u64::MAX
+ let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision (15-17 digits)
+ let big_dec_magnitude_str = "1.8e308"; // > f64::MAX (~1.7976931348623157e308)
+
+ // Test 1: High precision BigDecimal
+ let input = crate::operation::test_op::TestOpInput::builder()
+ .big_int(BigInteger::from_str(big_int_str).unwrap())
+ .big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
+ .build()
+ .unwrap();
+
+ let xml_body = crate::protocol_serde::shape_test_op::ser_test_op_op_input(&input).unwrap();
+ let serialized = String::from_utf8(xml_body.bytes().unwrap().to_vec()).unwrap();
+
+ assert!(serialized.contains(big_int_str));
+ assert!(serialized.contains(big_dec_precision_str));
+
+ // Test 2: Large magnitude BigDecimal - construct XML manually
+ let mut xml_response = String::from(r#""#);
+ xml_response.push_str(big_int_str);
+ xml_response.push_str(r#""#);
+ xml_response.push_str(big_dec_magnitude_str);
+ xml_response.push_str(r#""#);
+
+ let headers = ::aws_smithy_runtime_api::http::Headers::new();
+ let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
+ 200,
+ &headers,
+ xml_response.as_bytes()
+ ).unwrap();
+
+ assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
+ assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
+ """,
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `test BigInteger and BigDecimal round trip through serializers with awsJson1_1`() {
+ val model =
+ """
+ namespace test
+ use aws.protocols#awsJson1_1
+
+ @awsJson1_1
+ service TestService {
+ version: "2023-01-01",
+ operations: [TestOp]
+ }
+
+ operation TestOp {
+ input: TestInput,
+ output: TestOutput
+ }
+
+ structure TestInput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+
+ structure TestOutput {
+ bigInt: BigInteger,
+ bigDec: BigDecimal
+ }
+ """.asSmithyModel()
+
+ clientIntegrationTest(model) { _, rustCrate ->
+ rustCrate.unitTest("big_number_round_trip_aws_json") {
+ rawRust(
+ """
+ use aws_smithy_types::{BigInteger, BigDecimal};
+ use std::str::FromStr;
+
+ // Test values that exceed native type limits
+ let big_int_str = "99999999999999999999999999"; // > u64::MAX
+ let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision
+ let big_dec_magnitude_str = "1.8e308"; // > f64::MAX
+
+ // Test 1: High precision BigDecimal
+ let input = crate::operation::test_op::TestOpInput::builder()
+ .big_int(BigInteger::from_str(big_int_str).unwrap())
+ .big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
+ .build()
+ .unwrap();
+
+ let json_body = crate::protocol_serde::shape_test_op::ser_test_op_input(&input).unwrap();
+ let serialized = String::from_utf8(json_body.bytes().unwrap().to_vec()).unwrap();
+
+ assert!(serialized.contains(big_int_str));
+ assert!(serialized.contains(big_dec_precision_str));
+
+ // Test 2: Large magnitude BigDecimal
+ let mut json_response = String::from(r#"{"bigInt":"#);
+ json_response.push_str(big_int_str);
+ json_response.push_str(r#","bigDec":"#);
+ json_response.push_str(big_dec_magnitude_str);
+ json_response.push('}');
+
+ let headers = ::aws_smithy_runtime_api::http::Headers::new();
+ let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
+ 200,
+ &headers,
+ json_response.as_bytes()
+ ).unwrap();
+
+ assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
+ assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
+ """,
+ )
+ }
+ }
+ }
+}
diff --git a/codegen-core/common-test-models/big-numbers.smithy b/codegen-core/common-test-models/big-numbers.smithy
new file mode 100644
index 00000000000..e631f87061f
--- /dev/null
+++ b/codegen-core/common-test-models/big-numbers.smithy
@@ -0,0 +1,287 @@
+$version: "2.0"
+
+namespace com.amazonaws.bignumbers
+
+use aws.protocols#restJson1
+use smithy.test#httpRequestTests
+use smithy.test#httpResponseTests
+
+// Protocol tests for BigInteger and BigDecimal types.
+//
+// LIMITATION: Protocol test infrastructure has two precision constraints:
+// 1. Smithy model parser converts numeric literals in `params` to Java Number (f64), losing precision
+// 2. Protocol test validator uses serde_json which also truncates to f64
+//
+// Therefore these tests use:
+// - BigInteger: 18446744073709551616 (u64::MAX + 1) - tests arbitrary precision for integers
+// - BigDecimal: Values within f64 range - cannot test true arbitrary decimal precision here
+//
+// For comprehensive arbitrary precision testing including decimals > f64::MAX and high-precision
+// decimals, see BigNumberPrecisionTest.kt integration tests which test actual serialization/
+// deserialization without protocol test infrastructure limitations.
+
+@restJson1
+service BigNumberService {
+ version: "2023-01-01"
+ operations: [ProcessBigNumbers, ProcessNestedBigNumbers]
+}
+
+@http(uri: "/process", method: "POST")
+@httpRequestTests([
+ {
+ id: "BigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":123456789,\"bigDec\":123.456789}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: 123456789,
+ bigDec: 123.456789
+ }
+ },
+ {
+ id: "NegativeBigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":-987654321,\"bigDec\":-0.000000001}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: -987654321,
+ bigDec: -0.000000001
+ }
+ },
+ {
+ id: "ZeroBigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":0,\"bigDec\":0.0}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: 0,
+ bigDec: 0.0
+ }
+ },
+ {
+ id: "VeryLargeBigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":18446744073709551616,\"bigDec\":123456.789}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: 18446744073709551616,
+ bigDec: 123456.789
+ }
+ },
+ {
+ id: "ScientificNotationBigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":12300000000,\"bigDec\":4.56e-5}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: 12300000000,
+ bigDec: 0.0000456
+ }
+ },
+ {
+ id: "UppercaseScientificNotationBigNumbersInJsonRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/process",
+ body: "{\"bigInt\":9870000000000000,\"bigDec\":3.21E-10}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ bigInt: 9870000000000000,
+ bigDec: 0.000000000321
+ }
+ }
+])
+@httpResponseTests([
+ {
+ id: "BigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":999999999,\"ratio\":0.123456789}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: 999999999,
+ ratio: 0.123456789
+ }
+ },
+ {
+ id: "NegativeBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":-123456789,\"ratio\":-999.999}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: -123456789,
+ ratio: -999.999
+ }
+ },
+ {
+ id: "VeryLargeBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":18446744073709551616,\"ratio\":123456.789}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: 18446744073709551616,
+ ratio: 123456.789
+ }
+ },
+ {
+ id: "ZeroBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":0,\"ratio\":0.0}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: 0,
+ ratio: 0.0
+ }
+ },
+ {
+ id: "NullBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":null,\"ratio\":null}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {}
+ },
+ {
+ id: "ScientificNotationBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":1500000000000,\"ratio\":2.5E-8}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: 1500000000000,
+ ratio: 0.000000025
+ }
+ },
+ {
+ id: "UppercaseScientificNotationBigNumbersInJsonResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"result\":789000000000000000000,\"ratio\":1.23E-15}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ result: 789000000000000000000,
+ ratio: 0.00000000000000123
+ }
+ }
+])
+
+operation ProcessBigNumbers {
+ input: BigNumberInput
+ output: BigNumberOutput
+}
+
+structure BigNumberInput {
+ bigInt: BigInteger
+ bigDec: BigDecimal
+}
+
+structure BigNumberOutput {
+ result: BigInteger
+ ratio: BigDecimal
+}
+
+// Collections and nested structures
+list BigIntegerList {
+ member: BigInteger
+}
+
+list BigDecimalList {
+ member: BigDecimal
+}
+
+map StringToBigIntegerMap {
+ key: String
+ value: BigInteger
+}
+
+map StringToBigDecimalMap {
+ key: String
+ value: BigDecimal
+}
+
+structure NestedBigNumbers {
+ numbers: BigIntegerList
+ ratios: BigDecimalList
+ intMap: StringToBigIntegerMap
+ decMap: StringToBigDecimalMap
+}
+
+@http(uri: "/nested", method: "POST")
+@httpRequestTests([
+ {
+ id: "BigNumbersInCollectionsRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/nested",
+ body: "{\"numbers\":[1,2,3],\"ratios\":[1.1,2.2,3.3],\"intMap\":{\"a\":100,\"b\":200},\"decMap\":{\"x\":0.5,\"y\":1.5}}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ numbers: [1, 2, 3],
+ ratios: [1.1, 2.2, 3.3],
+ intMap: {a: 100, b: 200},
+ decMap: {x: 0.5, y: 1.5}
+ }
+ },
+ {
+ id: "LargeBigNumbersInCollectionsRequest",
+ protocol: restJson1,
+ method: "POST",
+ uri: "/nested",
+ body: "{\"numbers\":[18446744073709551616],\"ratios\":[123456.789],\"intMap\":{\"big\":18446744073709551616},\"decMap\":{\"precise\":0.123456789}}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ numbers: [18446744073709551616],
+ ratios: [123456.789],
+ intMap: {big: 18446744073709551616},
+ decMap: {precise: 0.123456789}
+ }
+ }
+])
+@httpResponseTests([
+ {
+ id: "BigNumbersInCollectionsResponse",
+ protocol: restJson1,
+ code: 200,
+ body: "{\"numbers\":[10,20,30],\"ratios\":[0.1,0.2,0.3],\"intMap\":{\"x\":1000},\"decMap\":{\"y\":99.99}}",
+ bodyMediaType: "application/json",
+ headers: {"Content-Type": "application/json"},
+ params: {
+ numbers: [10, 20, 30],
+ ratios: [0.1, 0.2, 0.3],
+ intMap: {x: 1000},
+ decMap: {y: 99.99}
+ }
+ }
+])
+operation ProcessNestedBigNumbers {
+ input: NestedBigNumbers
+ output: NestedBigNumbers
+}
diff --git a/codegen-core/common-test-models/misc.smithy b/codegen-core/common-test-models/misc.smithy
index 42329ba1d27..47d8e06abee 100644
--- a/codegen-core/common-test-models/misc.smithy
+++ b/codegen-core/common-test-models/misc.smithy
@@ -95,12 +95,11 @@ structure InnermostShape {
@required
aDouble: Double,
- // TODO(https://github.com/smithy-lang/smithy-rs/issues/312)
- // @required
- // aBigInteger: BigInteger,
+ @required
+ aBigInteger: BigInteger,
- // @required
- // aBigDecimal: BigDecimal,
+ @required
+ aBigDecimal: BigDecimal,
@required
aTimestamp: Timestamp,
diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt
index cdc1c621a57..b65cf23f381 100644
--- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt
+++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt
@@ -512,6 +512,10 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null)
fun dateTime(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("DateTime")
+ fun bigInteger(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("BigInteger")
+
+ fun bigDecimal(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("BigDecimal")
+
fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document")
fun format(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("date_time::Format")
diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt
index d2c30f6091e..7dc41523293 100644
--- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt
+++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolVisitor.kt
@@ -245,11 +245,11 @@ open class SymbolVisitor(
}
override fun bigIntegerShape(shape: BigIntegerShape?): Symbol {
- TODO("Not yet implemented: https://github.com/smithy-lang/smithy-rs/issues/312")
+ return RuntimeType.bigInteger(config.runtimeConfig).toSymbol()
}
override fun bigDecimalShape(shape: BigDecimalShape?): Symbol {
- TODO("Not yet implemented: https://github.com/smithy-lang/smithy-rs/issues/312")
+ return RuntimeType.bigDecimal(config.runtimeConfig).toSymbol()
}
override fun operationShape(shape: OperationShape): Symbol {
diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt
index c09bc545fc9..fce372c69c1 100644
--- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt
+++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt
@@ -14,6 +14,8 @@ import software.amazon.smithy.model.node.NullNode
import software.amazon.smithy.model.node.NumberNode
import software.amazon.smithy.model.node.ObjectNode
import software.amazon.smithy.model.node.StringNode
+import software.amazon.smithy.model.shapes.BigDecimalShape
+import software.amazon.smithy.model.shapes.BigIntegerShape
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.BooleanShape
import software.amazon.smithy.model.shapes.CollectionShape
@@ -544,6 +546,20 @@ class PrimitiveInstantiator(
}
is StringShape -> renderString(shape, data as StringNode)(this)
+ is BigIntegerShape -> {
+ val value = data.toString()
+ rustTemplate(
+ "<#{BigInteger} as ::std::str::FromStr>::from_str(${value.dq()}).expect(\"Invalid string for BigInteger\")",
+ "BigInteger" to RuntimeType.bigInteger(runtimeConfig),
+ )
+ }
+ is BigDecimalShape -> {
+ val value = data.toString()
+ rustTemplate(
+ "<#{BigDecimal} as ::std::str::FromStr>::from_str(${value.dq()}).expect(\"invalid string for BigDecimal\")",
+ "BigDecimal" to RuntimeType.bigDecimal(runtimeConfig),
+ )
+ }
is NumberShape ->
when (data) {
is StringNode -> {
diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/CborParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/CborParserGenerator.kt
index 58dbb9c77fa..b699030dccc 100644
--- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/CborParserGenerator.kt
+++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/CborParserGenerator.kt
@@ -5,7 +5,10 @@
package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse
+import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.codegen.core.Symbol
+import software.amazon.smithy.model.shapes.BigDecimalShape
+import software.amazon.smithy.model.shapes.BigIntegerShape
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.BooleanShape
import software.amazon.smithy.model.shapes.ByteShape
@@ -579,6 +582,20 @@ class CborParserGenerator(
is TimestampShape -> rust("decoder.timestamp()")
+ // BigInteger/BigDecimal are not supported with CBOR.
+ // The Smithy RPC v2 CBOR spec requires these to be encoded using CBOR tags 2/3/4
+ // (binary bignum representation), but aws-smithy-cbor doesn't implement these tags yet.
+ is BigIntegerShape ->
+ throw CodegenException(
+ "BigInteger is not supported with Concise Binary Object Representation (CBOR) protocol. " +
+ "See https://github.com/smithy-lang/smithy-rs/issues/4473",
+ )
+ is BigDecimalShape ->
+ throw CodegenException(
+ "BigDecimal is not supported with Concise Binary Object Representation (CBOR) protocol. " +
+ "See https://github.com/smithy-lang/smithy-rs/issues/4473",
+ )
+
// Aggregate shapes: https://smithy.io/2.0/spec/aggregate-types.html
is StructureShape -> deserializeStruct(target)
is CollectionShape -> deserializeCollection(target)
diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt
index 50569ccc776..7295494c07c 100644
--- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt
+++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt
@@ -6,6 +6,8 @@
package software.amazon.smithy.rust.codegen.core.smithy.protocols.parse
import software.amazon.smithy.codegen.core.Symbol
+import software.amazon.smithy.model.shapes.BigDecimalShape
+import software.amazon.smithy.model.shapes.BigIntegerShape
import software.amazon.smithy.model.shapes.BlobShape
import software.amazon.smithy.model.shapes.BooleanShape
import software.amazon.smithy.model.shapes.CollectionShape
@@ -118,6 +120,7 @@ class JsonParserGenerator(
"expect_bool_or_null" to smithyJson.resolve("deserialize::token::expect_bool_or_null"),
"expect_document" to smithyJson.resolve("deserialize::token::expect_document"),
"expect_number_or_null" to smithyJson.resolve("deserialize::token::expect_number_or_null"),
+ "expect_number_as_string_or_null" to smithyJson.resolve("deserialize::token::expect_number_as_string_or_null"),
"expect_start_array" to smithyJson.resolve("deserialize::token::expect_start_array"),
"expect_start_object" to smithyJson.resolve("deserialize::token::expect_start_object"),
"expect_string_or_null" to smithyJson.resolve("deserialize::token::expect_string_or_null"),
@@ -146,13 +149,15 @@ class JsonParserGenerator(
return protocolFunctions.deserializeFn(shape, fnNameSuffix) { fnName ->
val unusedMut = if (includedMembers.isEmpty()) "##[allow(unused_mut)] " else ""
rustBlockTemplate(
- "pub(crate) fn $fnName(value: &[u8], ${unusedMut}mut builder: #{Builder}) -> #{Result}<#{Builder}, #{Error}>",
+ """
+ pub(crate) fn $fnName(_value: &[u8], ${unusedMut}mut builder: #{Builder}) -> #{Result}<#{Builder}, #{Error}>
+ """,
"Builder" to builderSymbol,
*codegenScope,
) {
rustTemplate(
"""
- let mut tokens_owned = #{json_token_iter}(#{or_empty}(value)).peekable();
+ let mut tokens_owned = #{json_token_iter}(#{or_empty}(_value)).peekable();
let tokens = &mut tokens_owned;
#{expect_start_object}(tokens.next())?;
""",
@@ -173,15 +178,15 @@ class JsonParserGenerator(
}
return protocolFunctions.deserializeFn(shape, fnNameSuffix = "payload") { fnName ->
rustBlockTemplate(
- "pub(crate) fn $fnName(input: &[u8]) -> #{Result}<#{ReturnType}, #{Error}>",
+ "pub(crate) fn $fnName(_value: &[u8]) -> #{Result}<#{ReturnType}, #{Error}>",
*codegenScope,
"ReturnType" to returnSymbolToParse.symbol,
) {
val input =
if (shape is DocumentShape) {
- "input"
+ "_value"
} else {
- "#{or_empty}(input)"
+ "#{or_empty}(_value)"
}
rustTemplate(
@@ -296,6 +301,8 @@ class JsonParserGenerator(
when (val target = model.expectShape(memberShape.target)) {
is StringShape -> deserializeString(target)
is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope)
+ is BigIntegerShape -> deserializeBigInteger()
+ is BigDecimalShape -> deserializeBigDecimal()
is NumberShape -> deserializeNumber(target)
is BlobShape -> deserializeBlob(memberShape)
is TimestampShape -> deserializeTimestamp(memberShape)
@@ -374,6 +381,36 @@ class JsonParserGenerator(
}
}
+ private fun RustWriter.deserializeBigInteger() {
+ // Use expect_number_as_string_or_null to preserve arbitrary precision
+ // by extracting the raw JSON number string without converting to u64/i64/f64
+ rustTemplate(
+ """
+ #{expect_number_as_string_or_null}(tokens.next(), _value)?
+ .map(<#{BigInteger} as ::std::str::FromStr>::from_str)
+ .transpose()
+ .map_err(|e| #{Error}::custom(format!("invalid BigInteger: {e}")))?
+ """,
+ "BigInteger" to RuntimeType.bigInteger(codegenContext.runtimeConfig),
+ *codegenScope,
+ )
+ }
+
+ private fun RustWriter.deserializeBigDecimal() {
+ // Use expect_number_as_string_or_null to preserve arbitrary precision
+ // by extracting the raw JSON number string without converting to u64/i64/f64
+ rustTemplate(
+ """
+ #{expect_number_as_string_or_null}(tokens.next(), _value)?
+ .map(<#{BigDecimal} as ::std::str::FromStr>::from_str)
+ .transpose()
+ .map_err(|e| #{Error}::custom(format!("invalid BigDecimal: {e}")))?
+ """,
+ "BigDecimal" to RuntimeType.bigDecimal(codegenContext.runtimeConfig),
+ *codegenScope,
+ )
+ }
+
private fun RustWriter.deserializeTimestamp(member: MemberShape) {
val timestampFormat =
httpBindingResolver.timestampFormat(
@@ -397,7 +434,7 @@ class JsonParserGenerator(
protocolFunctions.deserializeFn(shape) { fnName ->
rustBlockTemplate(
"""
- pub(crate) fn $fnName<'a, I>(tokens: &mut #{Peekable}) -> #{Result}