1use crate::rpc::{typed_data::Data, TypedData};
2use serde::{de::Error, Deserialize};
3use serde_json::{from_str, Result, Value};
4use std::borrow::Cow;
5use std::fmt;
6use std::str::from_utf8;
7
8#[derive(Clone, Debug)]
10pub enum Body<'a> {
11 Empty,
13 String(Cow<'a, str>),
15 Json(Cow<'a, str>),
17 Bytes(Cow<'a, [u8]>),
19}
20
21impl Body<'_> {
22 pub fn default_content_type(&self) -> &str {
40 match self {
41 Body::Empty | Body::String(_) => "text/plain",
42 Body::Json(_) => "application/json",
43 Body::Bytes(_) => "application/octet-stream",
44 }
45 }
46
47 pub fn as_str(&self) -> Option<&str> {
61 match self {
62 Body::Empty => Some(""),
63 Body::String(s) => Some(s),
64 Body::Json(s) => Some(s),
65 Body::Bytes(b) => from_utf8(b).map(|s| s).ok(),
66 }
67 }
68
69 pub fn as_bytes(&self) -> &[u8] {
81 match self {
82 Body::Empty => &[],
83 Body::String(s) => s.as_bytes(),
84 Body::Json(s) => s.as_bytes(),
85 Body::Bytes(b) => b,
86 }
87 }
88
89 pub fn as_json<'b, T>(&'b self) -> Result<T>
108 where
109 T: Deserialize<'b>,
110 {
111 match self {
112 Body::Empty => from_str(""),
113 Body::String(s) => from_str(s.as_ref()),
114 Body::Json(s) => from_str(s.as_ref()),
115 Body::Bytes(b) => from_str(from_utf8(b).map_err(|e| {
116 ::serde_json::Error::custom(format!("body is not valid UTF-8: {}", e))
117 })?),
118 }
119 }
120}
121
122impl fmt::Display for Body<'_> {
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 write!(f, "{}", self.as_str().unwrap_or(""))
125 }
126}
127
128#[doc(hidden)]
129impl<'a> From<&'a TypedData> for Body<'a> {
130 fn from(data: &'a TypedData) -> Self {
131 match &data.data {
132 Some(Data::String(s)) => Body::String(Cow::from(s)),
133 Some(Data::Json(s)) => Body::Json(Cow::from(s)),
134 Some(Data::Bytes(b)) => Body::Bytes(Cow::from(b)),
135 Some(Data::Stream(s)) => Body::Bytes(Cow::from(s)),
136 _ => Body::Empty,
137 }
138 }
139}
140
141impl<'a> From<&'a str> for Body<'a> {
142 fn from(data: &'a str) -> Self {
143 Body::String(Cow::Borrowed(data))
144 }
145}
146
147impl From<String> for Body<'_> {
148 fn from(data: String) -> Self {
149 Body::String(Cow::Owned(data))
150 }
151}
152
153impl From<&Value> for Body<'_> {
154 fn from(data: &Value) -> Self {
155 Body::Json(Cow::Owned(data.to_string()))
156 }
157}
158
159impl From<Value> for Body<'_> {
160 fn from(data: Value) -> Self {
161 Body::Json(Cow::Owned(data.to_string()))
162 }
163}
164
165impl<'a> From<&'a [u8]> for Body<'a> {
166 fn from(data: &'a [u8]) -> Self {
167 Body::Bytes(Cow::Borrowed(data))
168 }
169}
170
171impl From<Vec<u8>> for Body<'_> {
172 fn from(data: Vec<u8>) -> Self {
173 Body::Bytes(Cow::Owned(data))
174 }
175}
176
177#[doc(hidden)]
178impl Into<TypedData> for Body<'_> {
179 fn into(self) -> TypedData {
180 TypedData {
181 data: match self {
182 Body::Empty => None,
183 Body::String(s) => Some(Data::String(s.into_owned())),
184 Body::Json(s) => Some(Data::Json(s.into_owned())),
185 Body::Bytes(b) => Some(Data::Bytes(b.into_owned())),
186 },
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use matches::matches;
195 use serde::{Deserialize, Serialize};
196 use serde_json::to_value;
197 use std::fmt::Write;
198
199 #[test]
200 fn it_has_a_default_content_type() {
201 let body = Body::Empty;
202 assert_eq!(body.default_content_type(), "text/plain");
203
204 let body = Body::String(Cow::Borrowed("test"));
205 assert_eq!(body.default_content_type(), "text/plain");
206
207 let body = Body::Json(Cow::Borrowed("1"));
208 assert_eq!(body.default_content_type(), "application/json");
209
210 let body = Body::Bytes(Cow::Borrowed(&[]));
211 assert_eq!(body.default_content_type(), "application/octet-stream");
212 }
213
214 #[test]
215 fn it_has_a_string_body() {
216 const BODY: &'static str = "test body";
217
218 let body: Body = BODY.into();
219 assert_eq!(body.as_str().unwrap(), BODY);
220
221 let data: TypedData = body.into();
222 assert_eq!(data.data, Some(Data::String(BODY.to_string())));
223 }
224
225 #[test]
226 fn it_has_a_json_body() {
227 #[derive(Serialize, Deserialize)]
228 struct SerializedData {
229 message: String,
230 };
231
232 const MESSAGE: &'static str = "test";
233
234 let data = SerializedData {
235 message: MESSAGE.to_string(),
236 };
237
238 let body: Body = ::serde_json::to_value(data).unwrap().into();
239 assert_eq!(body.as_json::<SerializedData>().unwrap().message, MESSAGE);
240
241 let data: TypedData = body.into();
242 assert_eq!(
243 data.data,
244 Some(Data::Json(r#"{"message":"test"}"#.to_string()))
245 );
246 }
247
248 #[test]
249 fn it_has_a_bytes_body() {
250 const BODY: &'static [u8] = &[1, 2, 3];
251
252 let body: Body = BODY.into();
253 assert_eq!(body.as_bytes(), BODY);
254
255 let data: TypedData = body.into();
256 assert_eq!(data.data, Some(Data::Bytes(BODY.to_vec())));
257 }
258
259 #[test]
260 fn it_displays_as_a_string() {
261 const BODY: &'static str = "test";
262
263 let body: Body = BODY.into();
264
265 let mut s = String::new();
266 write!(s, "{}", body).unwrap();
267
268 assert_eq!(s, BODY);
269 }
270
271 #[test]
272 fn it_converts_from_typed_data() {
273 let data = TypedData {
274 data: Some(Data::String("test".to_string())),
275 };
276
277 let body: Body = (&data).into();
278 assert!(matches!(body, Body::String(_)));
279 assert_eq!(body.as_str().unwrap(), "test");
280
281 let data = TypedData {
282 data: Some(Data::Json("test".to_string())),
283 };
284 let body: Body = (&data).into();
285 assert!(matches!(body, Body::Json(_)));
286 assert_eq!(body.as_str().unwrap(), "test");
287
288 let data = TypedData {
289 data: Some(Data::Bytes([0, 1, 2].to_vec())),
290 };
291 let body: Body = (&data).into();
292 assert!(matches!(body, Body::Bytes(_)));
293 assert_eq!(body.as_bytes(), [0, 1, 2]);
294
295 let data = TypedData {
296 data: Some(Data::Stream([0, 1, 2].to_vec())),
297 };
298 let body: Body = (&data).into();
299 assert!(matches!(body, Body::Bytes(_)));
300 assert_eq!(body.as_bytes(), [0, 1, 2]);
301 }
302
303 #[test]
304 fn it_converts_from_str() {
305 let body: Body = "test".into();
306 assert!(matches!(body, Body::String(Cow::Borrowed(_))));
307 assert_eq!(body.as_str().unwrap(), "test");
308 }
309
310 #[test]
311 fn it_converts_from_string() {
312 let body: Body = "test".to_string().into();
313 assert!(matches!(body, Body::String(Cow::Owned(_))));
314 assert_eq!(body.as_str().unwrap(), "test");
315 }
316
317 #[test]
318 fn it_converts_from_json() {
319 let body: Body = to_value("hello world").unwrap().into();
320 assert!(matches!(body, Body::Json(Cow::Owned(_))));
321 assert_eq!(body.as_str().unwrap(), r#""hello world""#);
322 }
323
324 #[test]
325 fn it_converts_from_u8_slice() {
326 let body: Body = [0, 1, 2][..].into();
327 assert!(matches!(body, Body::Bytes(Cow::Borrowed(_))));
328 assert_eq!(body.as_bytes(), [0, 1, 2]);
329 }
330
331 #[test]
332 fn it_converts_from_u8_vec() {
333 let body: Body = vec![0, 1, 2].into();
334 assert!(matches!(body, Body::Bytes(Cow::Owned(_))));
335 assert_eq!(body.as_bytes(), [0, 1, 2]);
336 }
337
338 #[test]
339 fn it_converts_to_typed_data() {
340 let body = Body::Empty;
341 let data: TypedData = body.into();
342 assert!(data.data.is_none());
343
344 let body: Body = "test".into();
345 let data: TypedData = body.into();
346 assert_eq!(data.data, Some(Data::String("test".to_string())));
347
348 let body: Body = to_value("test").unwrap().into();
349 let data: TypedData = body.into();
350 assert_eq!(data.data, Some(Data::Json(r#""test""#.to_string())));
351
352 let body: Body = vec![1, 2, 3].into();
353 let data: TypedData = body.into();
354 assert_eq!(data.data, Some(Data::Bytes([1, 2, 3].to_vec())));
355 }
356}