Skip to content

Commit 959b1a4

Browse files
AmitKulkarni23Amit Kulkarniaajtoddlandonxjamesrcoh
authored
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. (#4418)
## Motivation and Context Fixes #312 Smithy defines `bigInteger` and `bigDecimal` types for arbitrary-precision numbers, but smithy-rs had TODO placeholders instead of implementations. This prevented users from working with services that use these types. ## Description - Added `BigInteger` and `BigDecimal` runtime types in `aws-smithy-types` as string wrappers - Implemented JSON serialization/deserialization in codegen - String-based approach allows users to choose their preferred big number library (e.g., `num-bigint`, `rust_decimal`) - Added unit tests and integration tests with protocol test coverage ## Testing - Added unit tests in `SymbolVisitorTest.kt` - Created integration test model `big-numbers.smithy` with protocol tests - All codegen-core tests pass - All codegen-client-test integration tests pass ## Checklist - [ x] For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "client," "server," or both in the `applies_to` key. ---- _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: Amit Kulkarni <kulami@amazon.com> Co-authored-by: Aaron Todd <aajtodd@users.noreply.github.com> Co-authored-by: Landon James <lnj@amazon.com> Co-authored-by: Russell Cohen <rcoh@amazon.com>
1 parent 3f9f97f commit 959b1a4

File tree

48 files changed

+1705
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1705
-76
lines changed

.changelog/1763738215.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
applies_to:
3+
- client
4+
- aws-sdk-rust
5+
authors:
6+
- AmitKulkarni23
7+
references:
8+
- smithy-rs#312
9+
breaking: false
10+
new_feature: true
11+
bug_fix: false
12+
---
13+
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.

codegen-client-test/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ data class ClientTest(
6262

6363
val allCodegenTests = listOf(
6464
ClientTest("com.amazonaws.simple#SimpleService", "simple", dependsOn = listOf("simple.smithy")),
65+
ClientTest("com.amazonaws.bignumbers#BigNumberService", "big_numbers", dependsOn = listOf("big-numbers.smithy")),
6566
ClientTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"),
6667
ClientTest("com.amazonaws.ebs#Ebs", "ebs", dependsOn = listOf("ebs.json")),
6768
ClientTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"),
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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.client.smithy
7+
8+
import org.junit.jupiter.api.Test
9+
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
10+
import software.amazon.smithy.rust.codegen.core.rustlang.rawRust
11+
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
12+
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
13+
14+
class BigNumberPrecisionTest {
15+
@Test
16+
fun `test BigInteger and BigDecimal round trip through serializers with restJson1`() {
17+
val model =
18+
"""
19+
namespace test
20+
use aws.protocols#restJson1
21+
22+
@restJson1
23+
service TestService {
24+
version: "2026-01-01",
25+
operations: [TestOp]
26+
}
27+
28+
@http(uri: "/test", method: "POST")
29+
operation TestOp {
30+
input: TestInput,
31+
output: TestOutput
32+
}
33+
34+
structure TestInput {
35+
bigInt: BigInteger,
36+
bigDec: BigDecimal
37+
}
38+
39+
structure TestOutput {
40+
bigInt: BigInteger,
41+
bigDec: BigDecimal
42+
}
43+
""".asSmithyModel()
44+
45+
clientIntegrationTest(model) { _, rustCrate ->
46+
rustCrate.unitTest("big_number_round_trip") {
47+
rawRust(
48+
"""
49+
use aws_smithy_types::{BigInteger, BigDecimal};
50+
use std::str::FromStr;
51+
52+
// Test values that exceed native type limits
53+
let big_int_str = "99999999999999999999999999"; // > u64::MAX
54+
let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision (15-17 digits)
55+
let big_dec_magnitude_str = "1.8e308"; // > f64::MAX - tokenizer uses NaN for validation
56+
57+
// Test 1: High precision BigDecimal
58+
let input = crate::operation::test_op::TestOpInput::builder()
59+
.big_int(BigInteger::from_str(big_int_str).unwrap())
60+
.big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
61+
.build()
62+
.unwrap();
63+
64+
let json_body = crate::protocol_serde::shape_test_op::ser_test_op_input(&input).unwrap();
65+
let serialized = String::from_utf8(json_body.bytes().unwrap().to_vec()).unwrap();
66+
67+
assert!(serialized.contains(big_int_str));
68+
assert!(serialized.contains(big_dec_precision_str));
69+
70+
// Test 2: Large magnitude BigDecimal (> f64::MAX)
71+
let mut json_response = String::from(r#"{"bigInt":"#);
72+
json_response.push_str(big_int_str);
73+
json_response.push_str(r#","bigDec":"#);
74+
json_response.push_str(big_dec_magnitude_str);
75+
json_response.push('}');
76+
77+
let headers = ::aws_smithy_runtime_api::http::Headers::new();
78+
let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
79+
200,
80+
&headers,
81+
json_response.as_bytes()
82+
).unwrap();
83+
84+
assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
85+
assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
86+
""",
87+
)
88+
}
89+
}
90+
}
91+
92+
@Test
93+
fun `test BigInteger and BigDecimal round trip through serializers with restXml`() {
94+
val model =
95+
"""
96+
namespace test
97+
use aws.protocols#restXml
98+
99+
@restXml
100+
service TestService {
101+
version: "2026-01-01",
102+
operations: [TestOp]
103+
}
104+
105+
@http(uri: "/test", method: "POST")
106+
operation TestOp {
107+
input: TestInput,
108+
output: TestOutput
109+
}
110+
111+
structure TestInput {
112+
bigInt: BigInteger,
113+
bigDec: BigDecimal
114+
}
115+
116+
structure TestOutput {
117+
bigInt: BigInteger,
118+
bigDec: BigDecimal
119+
}
120+
""".asSmithyModel()
121+
122+
clientIntegrationTest(model) { _, rustCrate ->
123+
rustCrate.unitTest("big_number_round_trip_xml") {
124+
rawRust(
125+
"""
126+
use aws_smithy_types::{BigInteger, BigDecimal};
127+
use std::str::FromStr;
128+
129+
// Test values that exceed native type limits
130+
let big_int_str = "99999999999999999999999999"; // > u64::MAX
131+
let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision (15-17 digits)
132+
let big_dec_magnitude_str = "1.8e308"; // > f64::MAX (~1.7976931348623157e308)
133+
134+
// Test 1: High precision BigDecimal
135+
let input = crate::operation::test_op::TestOpInput::builder()
136+
.big_int(BigInteger::from_str(big_int_str).unwrap())
137+
.big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
138+
.build()
139+
.unwrap();
140+
141+
let xml_body = crate::protocol_serde::shape_test_op::ser_test_op_op_input(&input).unwrap();
142+
let serialized = String::from_utf8(xml_body.bytes().unwrap().to_vec()).unwrap();
143+
144+
assert!(serialized.contains(big_int_str));
145+
assert!(serialized.contains(big_dec_precision_str));
146+
147+
// Test 2: Large magnitude BigDecimal - construct XML manually
148+
let mut xml_response = String::from(r#"<TestOutput><bigInt>"#);
149+
xml_response.push_str(big_int_str);
150+
xml_response.push_str(r#"</bigInt><bigDec>"#);
151+
xml_response.push_str(big_dec_magnitude_str);
152+
xml_response.push_str(r#"</bigDec></TestOutput>"#);
153+
154+
let headers = ::aws_smithy_runtime_api::http::Headers::new();
155+
let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
156+
200,
157+
&headers,
158+
xml_response.as_bytes()
159+
).unwrap();
160+
161+
assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
162+
assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
163+
""",
164+
)
165+
}
166+
}
167+
}
168+
169+
@Test
170+
fun `test BigInteger and BigDecimal round trip through serializers with awsJson1_1`() {
171+
val model =
172+
"""
173+
namespace test
174+
use aws.protocols#awsJson1_1
175+
176+
@awsJson1_1
177+
service TestService {
178+
version: "2023-01-01",
179+
operations: [TestOp]
180+
}
181+
182+
operation TestOp {
183+
input: TestInput,
184+
output: TestOutput
185+
}
186+
187+
structure TestInput {
188+
bigInt: BigInteger,
189+
bigDec: BigDecimal
190+
}
191+
192+
structure TestOutput {
193+
bigInt: BigInteger,
194+
bigDec: BigDecimal
195+
}
196+
""".asSmithyModel()
197+
198+
clientIntegrationTest(model) { _, rustCrate ->
199+
rustCrate.unitTest("big_number_round_trip_aws_json") {
200+
rawRust(
201+
"""
202+
use aws_smithy_types::{BigInteger, BigDecimal};
203+
use std::str::FromStr;
204+
205+
// Test values that exceed native type limits
206+
let big_int_str = "99999999999999999999999999"; // > u64::MAX
207+
let big_dec_precision_str = "3.141592653589793238462643383279502884197"; // > f64 precision
208+
let big_dec_magnitude_str = "1.8e308"; // > f64::MAX
209+
210+
// Test 1: High precision BigDecimal
211+
let input = crate::operation::test_op::TestOpInput::builder()
212+
.big_int(BigInteger::from_str(big_int_str).unwrap())
213+
.big_dec(BigDecimal::from_str(big_dec_precision_str).unwrap())
214+
.build()
215+
.unwrap();
216+
217+
let json_body = crate::protocol_serde::shape_test_op::ser_test_op_input(&input).unwrap();
218+
let serialized = String::from_utf8(json_body.bytes().unwrap().to_vec()).unwrap();
219+
220+
assert!(serialized.contains(big_int_str));
221+
assert!(serialized.contains(big_dec_precision_str));
222+
223+
// Test 2: Large magnitude BigDecimal
224+
let mut json_response = String::from(r#"{"bigInt":"#);
225+
json_response.push_str(big_int_str);
226+
json_response.push_str(r#","bigDec":"#);
227+
json_response.push_str(big_dec_magnitude_str);
228+
json_response.push('}');
229+
230+
let headers = ::aws_smithy_runtime_api::http::Headers::new();
231+
let output = crate::protocol_serde::shape_test_op::de_test_op_http_response(
232+
200,
233+
&headers,
234+
json_response.as_bytes()
235+
).unwrap();
236+
237+
assert_eq!(output.big_int.unwrap().as_ref(), big_int_str);
238+
assert_eq!(output.big_dec.unwrap().as_ref(), big_dec_magnitude_str);
239+
""",
240+
)
241+
}
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)