azure_functions/bindings/
blob.rs
1use crate::{
2 http::Body,
3 rpc::{typed_data::Data, TypedData},
4};
5use serde::de::Error;
6use serde::Deserialize;
7use serde_json::{from_str, Result, Value};
8use std::borrow::Cow;
9use std::fmt;
10use std::str::from_utf8;
11
12#[derive(Debug, Clone)]
64pub struct Blob(TypedData);
65
66impl Blob {
67 pub fn as_str(&self) -> Option<&str> {
71 match &self.0.data {
72 Some(Data::String(s)) => Some(s),
73 Some(Data::Json(s)) => Some(s),
74 Some(Data::Bytes(b)) => from_utf8(b).ok(),
75 Some(Data::Stream(s)) => from_utf8(s).ok(),
76 _ => None,
77 }
78 }
79
80 pub fn as_bytes(&self) -> &[u8] {
82 match &self.0.data {
83 Some(Data::String(s)) => s.as_bytes(),
84 Some(Data::Json(s)) => s.as_bytes(),
85 Some(Data::Bytes(b)) => b,
86 Some(Data::Stream(s)) => s,
87 _ => panic!("unexpected data for blob content"),
88 }
89 }
90
91 pub fn as_json<'b, T>(&'b self) -> Result<T>
93 where
94 T: Deserialize<'b>,
95 {
96 from_str(
97 self.as_str()
98 .ok_or_else(|| ::serde_json::Error::custom("blob is not valid UTF-8"))?,
99 )
100 }
101}
102
103impl fmt::Display for Blob {
104 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105 write!(f, "{}", self.as_str().unwrap_or(""))
106 }
107}
108
109impl<'a> From<&'a str> for Blob {
110 fn from(content: &'a str) -> Self {
111 Blob(TypedData {
112 data: Some(Data::String(content.to_owned())),
113 })
114 }
115}
116
117impl From<String> for Blob {
118 fn from(content: String) -> Self {
119 Blob(TypedData {
120 data: Some(Data::String(content)),
121 })
122 }
123}
124
125impl From<&Value> for Blob {
126 fn from(content: &Value) -> Self {
127 Blob(TypedData {
128 data: Some(Data::Json(content.to_string())),
129 })
130 }
131}
132
133impl From<Value> for Blob {
134 fn from(content: Value) -> Self {
135 Blob(TypedData {
136 data: Some(Data::Json(content.to_string())),
137 })
138 }
139}
140
141impl<'a> From<&'a [u8]> for Blob {
142 fn from(content: &'a [u8]) -> Self {
143 Blob(TypedData {
144 data: Some(Data::Bytes(content.to_owned())),
145 })
146 }
147}
148
149impl From<Vec<u8>> for Blob {
150 fn from(content: Vec<u8>) -> Self {
151 Blob(TypedData {
152 data: Some(Data::Bytes(content)),
153 })
154 }
155}
156
157#[doc(hidden)]
158impl From<TypedData> for Blob {
159 fn from(data: TypedData) -> Self {
160 Blob(data)
161 }
162}
163
164impl Into<String> for Blob {
165 fn into(self) -> String {
166 match self.0.data {
167 Some(Data::String(s)) => s,
168 Some(Data::Json(s)) => s,
169 Some(Data::Bytes(b)) => {
170 String::from_utf8(b).expect("blob does not contain valid UTF-8 bytes")
171 }
172 Some(Data::Stream(s)) => {
173 String::from_utf8(s).expect("blob does not contain valid UTF-8 bytes")
174 }
175 _ => panic!("unexpected data for blob content"),
176 }
177 }
178}
179
180impl Into<Value> for Blob {
181 fn into(self) -> Value {
182 from_str(
183 self.as_str()
184 .expect("blob does not contain valid UTF-8 data"),
185 )
186 .expect("blob does not contain valid JSON data")
187 }
188}
189
190impl Into<Vec<u8>> for Blob {
191 fn into(self) -> Vec<u8> {
192 match self.0.data {
193 Some(Data::String(s)) => s.into_bytes(),
194 Some(Data::Json(s)) => s.into_bytes(),
195 Some(Data::Bytes(b)) => b,
196 Some(Data::Stream(s)) => s,
197 _ => panic!("unexpected data for blob content"),
198 }
199 }
200}
201
202impl<'a> Into<Body<'a>> for Blob {
203 fn into(self) -> Body<'a> {
204 match self.0.data {
205 Some(Data::String(s)) => s.into(),
206 Some(Data::Json(s)) => Body::Json(Cow::from(s)),
207 Some(Data::Bytes(b)) => b.into(),
208 Some(Data::Stream(s)) => s.into(),
209 _ => panic!("unexpected data for blob content"),
210 }
211 }
212}
213
214#[doc(hidden)]
215impl Into<TypedData> for Blob {
216 fn into(self) -> TypedData {
217 self.0
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use serde::{Deserialize, Serialize};
225 use serde_json::json;
226 use serde_json::to_value;
227 use std::fmt::Write;
228
229 #[test]
230 fn it_has_string_content() {
231 const BLOB: &'static str = "test blob";
232
233 let blob: Blob = BLOB.into();
234 assert_eq!(blob.as_str().unwrap(), BLOB);
235
236 let data: TypedData = blob.into();
237 assert_eq!(data.data, Some(Data::String(BLOB.to_string())));
238 }
239
240 #[test]
241 fn it_has_json_content() {
242 #[derive(Serialize, Deserialize)]
243 struct SerializedData {
244 message: String,
245 };
246
247 const MESSAGE: &'static str = "test";
248
249 let data = SerializedData {
250 message: MESSAGE.to_string(),
251 };
252
253 let blob: Blob = ::serde_json::to_value(data).unwrap().into();
254 assert_eq!(blob.as_json::<SerializedData>().unwrap().message, MESSAGE);
255
256 let data: TypedData = blob.into();
257 assert_eq!(
258 data.data,
259 Some(Data::Json(r#"{"message":"test"}"#.to_string()))
260 );
261 }
262
263 #[test]
264 fn it_has_bytes_content() {
265 const BLOB: &'static [u8] = &[1, 2, 3];
266
267 let blob: Blob = BLOB.into();
268 assert_eq!(blob.as_bytes(), BLOB);
269
270 let data: TypedData = blob.into();
271 assert_eq!(data.data, Some(Data::Bytes(BLOB.to_owned())));
272 }
273
274 #[test]
275 fn it_displays_as_a_string() {
276 const BLOB: &'static str = "test";
277
278 let blob: Blob = BLOB.into();
279
280 let mut s = String::new();
281 write!(s, "{}", blob).unwrap();
282
283 assert_eq!(s, BLOB);
284 }
285
286 #[test]
287 fn it_converts_from_str() {
288 let blob: Blob = "test".into();
289 assert_eq!(blob.as_str().unwrap(), "test");
290 }
291
292 #[test]
293 fn it_converts_from_string() {
294 let blob: Blob = "test".to_string().into();
295 assert_eq!(blob.as_str().unwrap(), "test");
296 }
297
298 #[test]
299 fn it_converts_from_json() {
300 let blob: Blob = to_value("hello world").unwrap().into();
301 assert_eq!(blob.as_str().unwrap(), r#""hello world""#);
302 }
303
304 #[test]
305 fn it_converts_from_u8_slice() {
306 let blob: Blob = [0, 1, 2][..].into();
307 assert_eq!(blob.as_bytes(), [0, 1, 2]);
308 }
309
310 #[test]
311 fn it_converts_from_u8_vec() {
312 let blob: Blob = vec![0, 1, 2].into();
313 assert_eq!(blob.as_bytes(), [0, 1, 2]);
314 }
315
316 #[test]
317 fn it_converts_from_typed_data() {
318 const BLOB: &'static str = "hello world!";
319
320 let data = TypedData {
321 data: Some(Data::String(BLOB.to_string())),
322 };
323
324 let blob: Blob = data.into();
325 assert_eq!(blob.as_str().unwrap(), BLOB);
326 }
327
328 #[test]
329 fn it_converts_to_string() {
330 let blob: Blob = "hello world!".into();
331 let s: String = blob.into();
332 assert_eq!(s, "hello world!");
333 }
334
335 #[test]
336 fn it_converts_to_json() {
337 let blob: Blob = json!({"hello": "world"}).into();
338 let value: Value = blob.into();
339 assert_eq!(value.to_string(), r#"{"hello":"world"}"#);
340 }
341
342 #[test]
343 fn it_converts_to_bytes() {
344 let blob: Blob = vec![1, 2, 3].into();
345 let bytes: Vec<u8> = blob.into();
346 assert_eq!(bytes, [1, 2, 3]);
347 }
348
349 #[test]
350 fn it_converts_to_body() {
351 let blob: Blob = "hello world!".into();
352 let body: Body = blob.into();
353 assert_eq!(body.as_str().unwrap(), "hello world!");
354
355 let blob: Blob = json!({"hello": "world"}).into();
356 let body: Body = blob.into();
357 assert_eq!(body.as_str().unwrap(), r#"{"hello":"world"}"#);
358
359 let blob: Blob = vec![1, 2, 3].into();
360 let body: Body = blob.into();
361 assert_eq!(body.as_bytes(), [1, 2, 3]);
362 }
363
364 #[test]
365 fn it_converts_to_typed_data() {
366 let blob: Blob = "test".into();
367 let data: TypedData = blob.into();
368 assert_eq!(data.data, Some(Data::String("test".to_string())));
369
370 let blob: Blob = to_value("test").unwrap().into();
371 let data: TypedData = blob.into();
372 assert_eq!(data.data, Some(Data::Json(r#""test""#.to_string())));
373
374 let blob: Blob = vec![1, 2, 3].into();
375 let data: TypedData = blob.into();
376 assert_eq!(data.data, Some(Data::Bytes(vec![1, 2, 3])));
377 }
378}