aws_smithy_types/
blob.rs

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