1use bytes::Bytes;
7use std::any::Any;
8
9#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)]
13pub struct Blob {
14 inner: Bytes,
15}
16
17impl Blob {
18 pub fn new<T: Into<Vec<u8>>>(input: T) -> Self {
20 Blob {
21 inner: input.into().into(),
22 }
23 }
24
25 pub fn from_maybe_shared<T: AsRef<[u8]> + 'static>(input: T) -> Self {
27 let mut input = Some(input);
28 let inner =
29 if let Some(bytes) = (&mut input as &mut dyn Any).downcast_mut::<Option<Bytes>>() {
30 bytes.take().expect("input is present")
31 } else {
32 Bytes::copy_from_slice(input.as_ref().expect("input is present").as_ref())
33 };
34 Blob { inner }
35 }
36
37 pub fn into_bytes(self) -> Bytes {
39 self.inner
40 }
41
42 pub fn into_inner(self) -> Vec<u8> {
44 self.inner.into()
45 }
46}
47
48impl AsRef<[u8]> for Blob {
49 fn as_ref(&self) -> &[u8] {
50 &self.inner
51 }
52}
53
54impl From<Vec<u8>> for Blob {
55 fn from(value: Vec<u8>) -> Self {
56 Blob::new(value)
57 }
58}
59
60impl From<Blob> for Vec<u8> {
61 fn from(value: Blob) -> Self {
62 value.into_inner()
63 }
64}
65
66impl From<&[u8]> for Blob {
67 fn from(value: &[u8]) -> Self {
68 Blob::new(value)
69 }
70}
71
72#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))]
73mod serde_serialize {
74 use super::*;
75 use serde::Serialize;
76
77 impl Serialize for Blob {
78 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79 where
80 S: serde::Serializer,
81 {
82 if serializer.is_human_readable() {
83 serializer.serialize_str(&crate::base64::encode(&self.inner))
84 } else {
85 serializer.serialize_bytes(&self.inner)
86 }
87 }
88 }
89}
90
91#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))]
92mod serde_deserialize {
93 use super::*;
94 use serde::{de::Visitor, Deserialize};
95
96 struct HumanReadableBlobVisitor;
97 impl<'de> Visitor<'de> for HumanReadableBlobVisitor {
98 type Value = Blob;
99 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 formatter.write_str("expected base64 encoded string")
101 }
102
103 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
104 where
105 E: serde::de::Error,
106 {
107 match crate::base64::decode(v) {
108 Ok(inner) => Ok(Blob::from(inner)),
109 Err(e) => Err(E::custom(e)),
110 }
111 }
112 }
113
114 struct NotHumanReadableBlobVisitor;
115 impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor {
116 type Value = Blob;
117 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 formatter.write_str("expected bytes")
119 }
120
121 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
122 where
123 E: serde::de::Error,
124 {
125 Ok(Blob::from(v))
126 }
127 }
128
129 impl<'de> Deserialize<'de> for Blob {
130 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
131 where
132 D: serde::Deserializer<'de>,
133 {
134 if deserializer.is_human_readable() {
135 deserializer.deserialize_str(HumanReadableBlobVisitor)
136 } else {
137 deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor)
138 }
139 }
140 }
141}
142
143#[cfg(test)]
144mod test {
145 use crate::Blob;
146 use bytes::Bytes;
147
148 #[test]
149 fn blob_conversion() {
150 let my_bytes: &[u8] = &[1u8, 2u8, 3u8];
151 let my_vec = vec![1u8, 2u8, 3u8];
152 let orig_vec = my_vec.clone();
153
154 let blob1: Blob = my_bytes.into();
155 let vec1: Vec<u8> = blob1.into();
156 assert_eq!(orig_vec, vec1);
157
158 let blob2: Blob = my_vec.into();
159 let vec2: Vec<u8> = blob2.into();
160 assert_eq!(orig_vec, vec2);
161 }
162
163 #[test]
164 fn blob_reuses_bytes() {
165 let bytes = Bytes::from_static(b"some shared bytes");
166 let original_ptr = bytes.as_ptr();
167
168 let blob = Blob::from_maybe_shared(bytes);
169 let bytes = blob.into_bytes();
170
171 assert_eq!(original_ptr, bytes.as_ptr());
172 }
173
174 #[test]
175 fn blob_new_accepts_borrowed_data() {
176 let bytes = vec![1, 2, 3];
177 let blob = Blob::new(bytes.as_slice());
178
179 assert_eq!(bytes, blob.as_ref());
180 }
181}
182
183#[cfg(all(
184 aws_sdk_unstable,
185 feature = "serde-serialize",
186 feature = "serde-deserialize"
187))]
188mod test_serde {
189 use crate::Blob;
190 use serde::{Deserialize, Serialize};
191
192 #[derive(Deserialize, Serialize, Debug, PartialEq)]
193 #[allow(dead_code)]
194 struct ForTest {
195 blob: Blob,
196 }
197
198 #[test]
199 fn human_readable_blob() {
200 let aws_in_base64 = r#"{"blob":"QVdT"}"#;
201 let for_test = ForTest {
202 blob: Blob {
203 inner: vec![b'A', b'W', b'S'].into(),
204 },
205 };
206 assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap());
207 assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64);
208 }
209
210 #[test]
211 fn not_human_readable_blob() {
212 use std::collections::HashMap;
213 use std::ffi::CString;
214
215 let for_test = ForTest {
216 blob: Blob {
217 inner: vec![b'A', b'W', b'S'].into(),
218 },
219 };
220 let mut buf = vec![];
221 let res = ciborium::ser::into_writer(&for_test, &mut buf);
222 assert!(res.is_ok());
223
224 let n: HashMap<String, CString> =
226 ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap();
227 assert!(n.get("blob").is_some());
228 assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref());
229
230 let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap();
231 assert_eq!(for_test, de);
232 }
233}