Skip to content

Commit

Permalink
Add serde support to Blob type (#2647)
Browse files Browse the repository at this point in the history
## 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 <[email protected]>
  • Loading branch information
thomas-k-cameron and jdisanti authored Jun 6, 2023
1 parent 74a7204 commit 77395c3
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 2 deletions.
5 changes: 4 additions & 1 deletion rust-runtime/aws-smithy-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[package]
name = "aws-smithy-types"
version = "0.0.0-smithy-rs-head"
authors = ["AWS Rust SDK Team <[email protected]>", "Russell Cohen <[email protected]>"]
authors = [
"AWS Rust SDK Team <[email protected]>",
"Russell Cohen <[email protected]>",
]
description = "Types for smithy-rs codegen."
edition = "2021"
license = "Apache-2.0"
Expand Down
4 changes: 3 additions & 1 deletion rust-runtime/aws-smithy-types/additional-ci
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#

# This script contains additional CI checks to run for this specific package

set -e

echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled"
cargo tree -d --edges normal --all-features

echo "### Checking whether the features are properly feature-gated"
! cargo tree -e no-dev | grep serde
125 changes: 125 additions & 0 deletions rust-runtime/aws-smithy-types/src/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,128 @@ impl AsRef<[u8]> for Blob {
&self.inner
}
}

#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
mod serde_serialize {
use super::*;
use crate::base64;
use serde::Serialize;

impl Serialize for Blob {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&crate::base64::encode(&self.inner))
} else {
serializer.serialize_bytes(&self.inner)
}
}
}
}

#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
mod serde_deserialize {
use super::*;
use crate::base64;
use serde::{de::Visitor, Deserialize};

struct HumanReadableBlobVisitor;
impl<'de> Visitor<'de> for HumanReadableBlobVisitor {
type Value = Blob;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("expected base64 encoded string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match base64::decode(v) {
Ok(inner) => Ok(Blob { inner }),
Err(e) => Err(E::custom(e)),
}
}
}

struct NotHumanReadableBlobVisitor;
impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor {
type Value = Blob;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("expected bytes")
}

fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Blob { inner: v })
}
}

impl<'de> Deserialize<'de> for Blob {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
deserializer.deserialize_str(HumanReadableBlobVisitor)
} else {
deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor)
}
}
}
}

#[cfg(test)]
#[cfg(all(
aws_sdk_unstable,
feature = "serde-serialize",
feature = "serde-deserialize"
))]
mod test_serde {
use crate::Blob;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Deserialize, Serialize, Debug, PartialEq)]
struct ForTest {
blob: Blob,
}

#[test]
fn human_readable_blob() {
let aws_in_base64 = r#"{"blob":"QVdT"}"#;
let for_test = ForTest {
blob: Blob {
inner: vec![b'A', b'W', b'S'],
},
};
assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap());
assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64);
}

#[test]
fn not_human_readable_blob() {
use std::ffi::CString;

let for_test = ForTest {
blob: Blob {
inner: vec![b'A', b'W', b'S'],
},
};
let mut buf = vec![];
let res = ciborium::ser::into_writer(&for_test, &mut buf);
assert!(res.is_ok());

// checks whether the bytes are deserialized properly
let n: HashMap<String, CString> =
ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap();
assert!(n.get("blob").is_some());
assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref());

let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap();
assert_eq!(for_test, de);
}
}

0 comments on commit 77395c3

Please sign in to comment.