Skip to content

Commit 77395c3

Browse files
Add serde support to Blob type (#2647)
## Motivation and Context This is a child PR of #2616 The changes that this PR introduces is same as the ones that were merged to `unstable-serde-support` branch before. Initially, we tried to make commit to unstable-serde-support branch and merge changes one by one in small PRs. However, in order to make it up to date with the main branch, we would need to go through a large PR of over 700 files. Thus, I decided to create individual PRs that commits directly to `main` branch. ## Description - Implements `serde` support to `Blob` ## Testing - Test checks whether the serialized/de-serialized data matches with the expected value ## Checklist NA ---- _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: John DiSanti <johndisanti@gmail.com>
1 parent 74a7204 commit 77395c3

File tree

3 files changed

+132
-2
lines changed

3 files changed

+132
-2
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[package]
22
name = "aws-smithy-types"
33
version = "0.0.0-smithy-rs-head"
4-
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Russell Cohen <rcoh@amazon.com>"]
4+
authors = [
5+
"AWS Rust SDK Team <aws-sdk-rust@amazon.com>",
6+
"Russell Cohen <rcoh@amazon.com>",
7+
]
58
description = "Types for smithy-rs codegen."
69
edition = "2021"
710
license = "Apache-2.0"

rust-runtime/aws-smithy-types/additional-ci

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
#
66

77
# This script contains additional CI checks to run for this specific package
8-
98
set -e
109

1110
echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled"
1211
cargo tree -d --edges normal --all-features
12+
13+
echo "### Checking whether the features are properly feature-gated"
14+
! cargo tree -e no-dev | grep serde

rust-runtime/aws-smithy-types/src/blob.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,128 @@ impl AsRef<[u8]> for Blob {
3030
&self.inner
3131
}
3232
}
33+
34+
#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
35+
mod serde_serialize {
36+
use super::*;
37+
use crate::base64;
38+
use serde::Serialize;
39+
40+
impl Serialize for Blob {
41+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42+
where
43+
S: serde::Serializer,
44+
{
45+
if serializer.is_human_readable() {
46+
serializer.serialize_str(&crate::base64::encode(&self.inner))
47+
} else {
48+
serializer.serialize_bytes(&self.inner)
49+
}
50+
}
51+
}
52+
}
53+
54+
#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
55+
mod serde_deserialize {
56+
use super::*;
57+
use crate::base64;
58+
use serde::{de::Visitor, Deserialize};
59+
60+
struct HumanReadableBlobVisitor;
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(E::custom(e)),
74+
}
75+
}
76+
}
77+
78+
struct NotHumanReadableBlobVisitor;
79+
impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor {
80+
type Value = Blob;
81+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82+
formatter.write_str("expected bytes")
83+
}
84+
85+
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
86+
where
87+
E: serde::de::Error,
88+
{
89+
Ok(Blob { inner: v })
90+
}
91+
}
92+
93+
impl<'de> Deserialize<'de> for Blob {
94+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95+
where
96+
D: serde::Deserializer<'de>,
97+
{
98+
if deserializer.is_human_readable() {
99+
deserializer.deserialize_str(HumanReadableBlobVisitor)
100+
} else {
101+
deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor)
102+
}
103+
}
104+
}
105+
}
106+
107+
#[cfg(test)]
108+
#[cfg(all(
109+
aws_sdk_unstable,
110+
feature = "serde-serialize",
111+
feature = "serde-deserialize"
112+
))]
113+
mod test_serde {
114+
use crate::Blob;
115+
use serde::{Deserialize, Serialize};
116+
use std::collections::HashMap;
117+
118+
#[derive(Deserialize, Serialize, Debug, PartialEq)]
119+
struct ForTest {
120+
blob: Blob,
121+
}
122+
123+
#[test]
124+
fn human_readable_blob() {
125+
let aws_in_base64 = r#"{"blob":"QVdT"}"#;
126+
let for_test = ForTest {
127+
blob: Blob {
128+
inner: vec![b'A', b'W', b'S'],
129+
},
130+
};
131+
assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap());
132+
assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64);
133+
}
134+
135+
#[test]
136+
fn not_human_readable_blob() {
137+
use std::ffi::CString;
138+
139+
let for_test = ForTest {
140+
blob: Blob {
141+
inner: vec![b'A', b'W', b'S'],
142+
},
143+
};
144+
let mut buf = vec![];
145+
let res = ciborium::ser::into_writer(&for_test, &mut buf);
146+
assert!(res.is_ok());
147+
148+
// checks whether the bytes are deserialized properly
149+
let n: HashMap<String, CString> =
150+
ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap();
151+
assert!(n.get("blob").is_some());
152+
assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref());
153+
154+
let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap();
155+
assert_eq!(for_test, de);
156+
}
157+
}

0 commit comments

Comments
 (0)