Skip to content

Commit bf7333d

Browse files
Merge commit 'c203d2e3eed2b989281346455757a467329fbfa2' into RFC30/kotlin-codegen-core
2 parents 2a454e8 + c203d2e commit bf7333d

File tree

7 files changed

+873
-539
lines changed

7 files changed

+873
-539
lines changed

rust-runtime/aws-smithy-types/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ ryu = "1.0.5"
1414
time = { version = "0.3.4", features = ["parsing"] }
1515
base64-simd = "0.8"
1616

17+
18+
[target."cfg(aws_sdk_unstable)".dependencies.serde]
19+
version = "1"
20+
features = ["derive"]
21+
1722
[dev-dependencies]
1823
base64 = "0.13.0"
1924
lazy_static = "1.4"
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#[cfg(all(
7+
aws_sdk_unstable,
8+
any(feature = "serde-deserialize", feature = "serde-serialize")
9+
))]
10+
use crate::base64;
11+
#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
12+
use serde::Serialize;
13+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
14+
use serde::{de::Visitor, Deserialize};
15+
/// Binary Blob Type
16+
///
17+
/// Blobs represent protocol-agnostic binary content.
18+
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
19+
pub struct Blob {
20+
inner: Vec<u8>,
21+
}
22+
23+
impl Blob {
24+
/// Creates a new blob from the given `input`.
25+
pub fn new<T: Into<Vec<u8>>>(input: T) -> Self {
26+
Blob {
27+
inner: input.into(),
28+
}
29+
}
30+
31+
/// Consumes the `Blob` and returns a `Vec<u8>` with its contents.
32+
pub fn into_inner(self) -> Vec<u8> {
33+
self.inner
34+
}
35+
}
36+
37+
impl AsRef<[u8]> for Blob {
38+
fn as_ref(&self) -> &[u8] {
39+
&self.inner
40+
}
41+
}
42+
43+
#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
44+
impl Serialize for Blob {
45+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
46+
where
47+
S: serde::Serializer,
48+
{
49+
if serializer.is_human_readable() {
50+
serializer.serialize_str(&crate::base64::encode(&self.inner))
51+
} else {
52+
serializer.serialize_bytes(&self.inner)
53+
}
54+
}
55+
}
56+
57+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
58+
struct HumanReadableBlobVisitor;
59+
60+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
61+
impl<'de> Visitor<'de> for HumanReadableBlobVisitor {
62+
type Value = Blob;
63+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64+
formatter.write_str("expected base64 encoded string")
65+
}
66+
67+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
68+
where
69+
E: serde::de::Error,
70+
{
71+
match base64::decode(v) {
72+
Ok(inner) => Ok(Blob { inner }),
73+
Err(e) => Err(serde::de::Error::custom(e)),
74+
}
75+
}
76+
}
77+
78+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
79+
struct NotHumanReadableBlobVisitor;
80+
81+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
82+
impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor {
83+
type Value = Blob;
84+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85+
formatter.write_str("expected base64 encoded string")
86+
}
87+
88+
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
89+
where
90+
E: serde::de::Error,
91+
{
92+
Ok(Blob { inner: v })
93+
}
94+
}
95+
96+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
97+
impl<'de> Deserialize<'de> for Blob {
98+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99+
where
100+
D: serde::Deserializer<'de>,
101+
{
102+
if deserializer.is_human_readable() {
103+
deserializer.deserialize_str(HumanReadableBlobVisitor)
104+
} else {
105+
deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor)
106+
}
107+
}
108+
}
109+
110+
#[cfg(test)]
111+
#[cfg(all(
112+
aws_sdk_unstable,
113+
feature = "serde-serialize",
114+
feature = "serde-deserialize"
115+
))]
116+
mod test {
117+
use crate::Blob;
118+
use serde::{Deserialize, Serialize};
119+
use std::collections::HashMap;
120+
121+
#[derive(Deserialize, Serialize, Debug, PartialEq)]
122+
struct ForTest {
123+
blob: Blob,
124+
}
125+
126+
#[test]
127+
fn human_readable_blob() {
128+
let aws_in_base64 = r#"{"blob":"QVdT"}"#;
129+
let for_test = ForTest {
130+
blob: Blob {
131+
inner: vec![b'A', b'W', b'S'],
132+
},
133+
};
134+
assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap());
135+
assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64);
136+
}
137+
138+
#[test]
139+
fn not_human_readable_blob() {
140+
use std::ffi::CString;
141+
142+
let for_test = ForTest {
143+
blob: Blob {
144+
inner: vec![b'A', b'W', b'S'],
145+
},
146+
};
147+
let mut buf = vec![];
148+
let res = ciborium::ser::into_writer(&for_test, &mut buf);
149+
assert!(res.is_ok());
150+
151+
// checks whether the bytes are deserialiezd properly
152+
let n: HashMap<String, CString> =
153+
ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap();
154+
assert!(n.get("blob").is_some());
155+
assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref());
156+
157+
let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap();
158+
assert_eq!(for_test, de);
159+
}
160+
}

rust-runtime/aws-smithy-types/src/date_time/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ use std::time::Duration;
1616
use std::time::SystemTime;
1717
use std::time::UNIX_EPOCH;
1818

19+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
20+
mod de;
1921
mod format;
22+
#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
23+
mod ser;
24+
2025
pub use self::format::DateTimeFormatError;
2126
pub use self::format::DateTimeParseError;
2227

@@ -552,4 +557,50 @@ mod test {
552557
SystemTime::try_from(date_time).unwrap()
553558
);
554559
}
560+
561+
#[cfg(all(
562+
test,
563+
aws_sdk_unstable,
564+
feature = "serde-deserialize",
565+
feature = "serde-serialize"
566+
))]
567+
#[test]
568+
fn human_readable_datetime() {
569+
use serde::{Deserialize, Serialize};
570+
571+
let datetime = DateTime::from_secs(1576540098);
572+
#[derive(Serialize, Deserialize, PartialEq)]
573+
struct Test {
574+
datetime: DateTime,
575+
}
576+
let datetime_json = r#"{"datetime":"2019-12-16T23:48:18Z"}"#;
577+
assert!(serde_json::to_string(&Test { datetime }).ok() == Some(datetime_json.to_string()));
578+
579+
let test = serde_json::from_str::<Test>(&datetime_json).ok();
580+
assert!(test.is_some());
581+
assert!(test.unwrap().datetime == datetime);
582+
}
583+
584+
/// checks that they are serialized into tuples
585+
#[cfg(all(
586+
test,
587+
aws_sdk_unstable,
588+
feature = "serde-deserialize",
589+
feature = "serde-serialize"
590+
))]
591+
#[test]
592+
fn not_human_readable_datetime() {
593+
let cbor = ciborium::value::Value::Array(vec![
594+
ciborium::value::Value::Integer(1576540098i64.into()),
595+
ciborium::value::Value::Integer(0u32.into()),
596+
]);
597+
let datetime = DateTime::from_secs(1576540098);
598+
599+
let mut buf1 = vec![];
600+
let mut buf2 = vec![];
601+
let res1 = ciborium::ser::into_writer(&datetime, &mut buf1);
602+
let res2 = ciborium::ser::into_writer(&cbor, &mut buf2);
603+
assert!(res1.is_ok() && res2.is_ok());
604+
assert!(buf1 == buf2, "{:#?}", (buf1, buf2));
605+
}
555606
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
use crate::Number;
7+
use std::collections::HashMap;
8+
9+
/* ANCHOR: document */
10+
11+
/// Document Type
12+
///
13+
/// Document types represents protocol-agnostic open content that is accessed like JSON data.
14+
/// Open content is useful for modeling unstructured data that has no schema, data that can't be
15+
/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model.
16+
/// The serialization format of a document is an implementation detail of a protocol.
17+
#[derive(Debug, Clone, PartialEq)]
18+
#[cfg_attr(
19+
all(aws_sdk_unstable, feature = "serde-serialize"),
20+
derive(serde::Serialize)
21+
)]
22+
#[cfg_attr(
23+
all(aws_sdk_unstable, feature = "serde-deserialize"),
24+
derive(serde::Deserialize)
25+
)]
26+
#[cfg_attr(
27+
any(
28+
all(aws_sdk_unstable, feature = "serde-deserialize"),
29+
all(aws_sdk_unstable, feature = "serde-serialize")
30+
),
31+
serde(untagged)
32+
)]
33+
pub enum Document {
34+
/// JSON object
35+
Object(HashMap<String, Document>),
36+
/// JSON array
37+
Array(Vec<Document>),
38+
/// JSON number
39+
Number(Number),
40+
/// JSON string
41+
String(String),
42+
/// JSON boolean
43+
Bool(bool),
44+
/// JSON null
45+
Null,
46+
}
47+
48+
impl From<bool> for Document {
49+
fn from(value: bool) -> Self {
50+
Document::Bool(value)
51+
}
52+
}
53+
54+
impl From<String> for Document {
55+
fn from(value: String) -> Self {
56+
Document::String(value)
57+
}
58+
}
59+
60+
impl From<Vec<Document>> for Document {
61+
fn from(values: Vec<Document>) -> Self {
62+
Document::Array(values)
63+
}
64+
}
65+
66+
impl From<HashMap<String, Document>> for Document {
67+
fn from(values: HashMap<String, Document>) -> Self {
68+
Document::Object(values)
69+
}
70+
}
71+
72+
impl From<u64> for Document {
73+
fn from(value: u64) -> Self {
74+
Document::Number(Number::PosInt(value))
75+
}
76+
}
77+
78+
impl From<i64> for Document {
79+
fn from(value: i64) -> Self {
80+
Document::Number(Number::NegInt(value))
81+
}
82+
}
83+
84+
impl From<i32> for Document {
85+
fn from(value: i32) -> Self {
86+
Document::Number(Number::NegInt(value as i64))
87+
}
88+
}
89+
90+
/* ANCHOR END: document */
91+
92+
#[cfg(test)]
93+
mod test {
94+
/// checks if a) serialization of json suceeds and b) it is compatible with serde_json
95+
#[test]
96+
#[cfg(all(
97+
aws_sdk_unstable,
98+
feature = "serde-serialize",
99+
feature = "serde-deserialize"
100+
))]
101+
fn serialize_json() {
102+
use crate::Document;
103+
use crate::Number;
104+
use std::collections::HashMap;
105+
let mut map: HashMap<String, Document> = HashMap::new();
106+
// string
107+
map.insert("hello".into(), "world".to_string().into());
108+
// numbers
109+
map.insert("pos_int".into(), Document::Number(Number::PosInt(1).into()));
110+
map.insert(
111+
"neg_int".into(),
112+
Document::Number(Number::NegInt(-1).into()),
113+
);
114+
map.insert(
115+
"float".into(),
116+
Document::Number(Number::Float(0.1 + 0.2).into()),
117+
);
118+
// booleans
119+
map.insert("true".into(), true.into());
120+
map.insert("false".into(), false.into());
121+
// check if array with different datatypes would succeed
122+
map.insert(
123+
"array".into(),
124+
vec![
125+
map.clone().into(),
126+
"hello-world".to_string().into(),
127+
true.into(),
128+
false.into(),
129+
]
130+
.into(),
131+
);
132+
// map
133+
map.insert("map".into(), map.clone().into());
134+
// null
135+
map.insert("null".into(), Document::Null);
136+
let obj = Document::Object(map);
137+
// comparing string isnt going to work since there is no gurantee for the ordering of the keys
138+
let target_file = include_str!("../test_data/serialize_document.json");
139+
let json: Result<serde_json::Value, _> = serde_json::from_str(target_file);
140+
// serializer
141+
assert_eq!(serde_json::to_value(&obj).unwrap(), json.unwrap());
142+
let doc: Result<Document, _> = serde_json::from_str(target_file);
143+
assert_eq!(obj, doc.unwrap());
144+
}
145+
}

0 commit comments

Comments
 (0)