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