aws_lambda_events/encodings/
http.rs1use base64::display::Base64Display;
2use bytes::Bytes;
3use http_body::{Body as HttpBody, SizeHint};
4use serde::{
5 de::{Deserialize, Deserializer, Error as DeError, Visitor},
6 ser::{Error as SerError, Serialize, Serializer},
7};
8use std::{borrow::Cow, mem::take, ops::Deref, pin::Pin, task::Poll};
9
10#[non_exhaustive]
64#[derive(Debug, Default, Eq, PartialEq)]
65pub enum Body {
66 #[default]
68 Empty,
69 Text(String),
71 Binary(Vec<u8>),
73}
74
75impl Body {
76 pub fn from_maybe_encoded(is_base64_encoded: bool, body: &str) -> Body {
83 use base64::Engine;
84
85 if is_base64_encoded {
86 Body::from(
87 ::base64::engine::general_purpose::STANDARD
88 .decode(body)
89 .expect("failed to decode aws base64 encoded body"),
90 )
91 } else {
92 Body::from(body)
93 }
94 }
95}
96
97impl From<()> for Body {
98 fn from(_: ()) -> Self {
99 Body::Empty
100 }
101}
102
103impl<'a> From<&'a str> for Body {
104 fn from(s: &'a str) -> Self {
105 Body::Text(s.into())
106 }
107}
108
109impl From<String> for Body {
110 fn from(b: String) -> Self {
111 Body::Text(b)
112 }
113}
114
115impl From<Cow<'static, str>> for Body {
116 #[inline]
117 fn from(cow: Cow<'static, str>) -> Body {
118 match cow {
119 Cow::Borrowed(b) => Body::from(b.to_owned()),
120 Cow::Owned(o) => Body::from(o),
121 }
122 }
123}
124
125impl From<Cow<'static, [u8]>> for Body {
126 #[inline]
127 fn from(cow: Cow<'static, [u8]>) -> Body {
128 match cow {
129 Cow::Borrowed(b) => Body::from(b),
130 Cow::Owned(o) => Body::from(o),
131 }
132 }
133}
134
135impl From<Vec<u8>> for Body {
136 fn from(b: Vec<u8>) -> Self {
137 Body::Binary(b)
138 }
139}
140
141impl<'a> From<&'a [u8]> for Body {
142 fn from(b: &'a [u8]) -> Self {
143 Body::Binary(b.to_vec())
144 }
145}
146
147impl Deref for Body {
148 type Target = [u8];
149
150 #[inline]
151 fn deref(&self) -> &Self::Target {
152 self.as_ref()
153 }
154}
155
156impl AsRef<[u8]> for Body {
157 #[inline]
158 fn as_ref(&self) -> &[u8] {
159 match self {
160 Body::Empty => &[],
161 Body::Text(ref bytes) => bytes.as_ref(),
162 Body::Binary(ref bytes) => bytes.as_ref(),
163 }
164 }
165}
166
167impl Clone for Body {
168 fn clone(&self) -> Self {
169 match self {
170 Body::Empty => Body::Empty,
171 Body::Text(ref bytes) => Body::Text(bytes.clone()),
172 Body::Binary(ref bytes) => Body::Binary(bytes.clone()),
173 }
174 }
175}
176
177impl Serialize for Body {
178 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179 where
180 S: Serializer,
181 {
182 match self {
183 Body::Text(data) => {
184 serializer.serialize_str(::std::str::from_utf8(data.as_ref()).map_err(S::Error::custom)?)
185 }
186 Body::Binary(data) => {
187 serializer.collect_str(&Base64Display::new(data, &base64::engine::general_purpose::STANDARD))
188 }
189 Body::Empty => serializer.serialize_unit(),
190 }
191 }
192}
193
194impl<'de> Deserialize<'de> for Body {
195 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
196 where
197 D: Deserializer<'de>,
198 {
199 struct BodyVisitor;
200
201 impl Visitor<'_> for BodyVisitor {
202 type Value = Body;
203
204 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 formatter.write_str("string")
206 }
207
208 fn visit_str<E>(self, value: &str) -> Result<Body, E>
209 where
210 E: DeError,
211 {
212 Ok(Body::from(value))
213 }
214 }
215
216 deserializer.deserialize_str(BodyVisitor)
217 }
218}
219
220impl HttpBody for Body {
221 type Data = Bytes;
222 type Error = super::Error;
223
224 fn is_end_stream(&self) -> bool {
225 matches!(self, Body::Empty)
226 }
227
228 fn size_hint(&self) -> SizeHint {
229 match self {
230 Body::Empty => SizeHint::default(),
231 Body::Text(ref s) => SizeHint::with_exact(s.len() as u64),
232 Body::Binary(ref b) => SizeHint::with_exact(b.len() as u64),
233 }
234 }
235
236 fn poll_frame(
237 self: Pin<&mut Self>,
238 _cx: &mut std::task::Context<'_>,
239 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
240 let body = take(self.get_mut());
241 Poll::Ready(match body {
242 Body::Empty => None,
243 Body::Text(s) => Some(Ok(http_body::Frame::data(s.into()))),
244 Body::Binary(b) => Some(Ok(http_body::Frame::data(b.into()))),
245 })
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use std::collections::HashMap;
253
254 #[test]
255 fn body_has_default() {
256 assert_eq!(Body::default(), Body::Empty);
257 }
258
259 #[test]
260 fn from_unit() {
261 assert_eq!(Body::from(()), Body::Empty);
262 }
263
264 #[test]
265 fn from_str() {
266 match Body::from(String::from("foo").as_str()) {
267 Body::Text(_) => (),
268 not => panic!("expected Body::Text(...) got {not:?}"),
269 }
270 }
271
272 #[test]
273 fn from_string() {
274 match Body::from(String::from("foo")) {
275 Body::Text(_) => (),
276 not => panic!("expected Body::Text(...) got {not:?}"),
277 }
278 }
279
280 #[test]
281 fn from_cow_str() {
282 match Body::from(Cow::from("foo")) {
283 Body::Text(_) => (),
284 not => panic!("expected Body::Text(...) got {not:?}"),
285 }
286 }
287
288 #[test]
289 fn from_cow_bytes() {
290 match Body::from(Cow::from("foo".as_bytes())) {
291 Body::Binary(_) => (),
292 not => panic!("expected Body::Binary(...) got {not:?}"),
293 }
294 }
295
296 #[test]
297 fn from_bytes() {
298 match Body::from("foo".as_bytes()) {
299 Body::Binary(_) => (),
300 not => panic!("expected Body::Binary(...) got {not:?}"),
301 }
302 }
303
304 #[test]
305 fn serialize_text() {
306 let mut map = HashMap::new();
307 map.insert("foo", Body::from("bar"));
308 assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":"bar"}"#);
309 }
310
311 #[test]
312 fn serialize_binary() {
313 let mut map = HashMap::new();
314 map.insert("foo", Body::from("bar".as_bytes()));
315 assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":"YmFy"}"#);
316 }
317
318 #[test]
319 fn serialize_empty() {
320 let mut map = HashMap::new();
321 map.insert("foo", Body::Empty);
322 assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":null}"#);
323 }
324
325 #[test]
326 fn serialize_from_maybe_encoded() {
327 match Body::from_maybe_encoded(false, "foo") {
328 Body::Text(_) => (),
329 not => panic!("expected Body::Text(...) got {not:?}"),
330 }
331
332 match Body::from_maybe_encoded(true, "Zm9v") {
333 Body::Binary(b) => assert_eq!(&[102, 111, 111], b.as_slice()),
334 not => panic!("expected Body::Text(...) got {not:?}"),
335 }
336 }
337}