httpclient/body/
memory.rs

1use crate::sanitize::sanitize_value;
2use crate::InMemoryResult;
3use hyper::body::Bytes;
4use serde::de::{DeserializeOwned, Error};
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::borrow::Cow;
8use std::hash::Hasher;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(untagged)]
12#[derive(Default)]
13pub enum InMemoryBody {
14    #[default]
15    Empty,
16    // json must come before bytes, otherwise Recorder deserialization gets messed up, see
17    // response::memory::test_deserialize
18    Json(Value),
19    Bytes(Vec<u8>),
20    Text(String),
21}
22
23impl TryInto<String> for InMemoryBody {
24    type Error = crate::InMemoryError;
25
26    fn try_into(self) -> InMemoryResult<String> {
27        match self {
28            InMemoryBody::Empty => Ok(String::new()),
29            InMemoryBody::Bytes(b) => String::from_utf8(b).map_err(std::convert::Into::into),
30            InMemoryBody::Text(s) => Ok(s),
31            InMemoryBody::Json(val) => match val {
32                Value::String(s) => Ok(s),
33                _ => serde_json::to_string(&val).map_err(std::convert::Into::into),
34            },
35        }
36    }
37}
38
39impl TryInto<Bytes> for InMemoryBody {
40    type Error = crate::InMemoryError;
41
42    fn try_into(self) -> InMemoryResult<Bytes> {
43        match self {
44            InMemoryBody::Empty => Ok(Bytes::new()),
45            InMemoryBody::Bytes(b) => Ok(Bytes::from(b)),
46            InMemoryBody::Text(s) => Ok(Bytes::from(s)),
47            InMemoryBody::Json(val) => {
48                if let Value::Array(a) = &val {
49                    if a.iter().all(|v| v.is_number()) {
50                        let mut bytes = Vec::with_capacity(a.len());
51                        for v in a {
52                            bytes.push(v.as_u64().unwrap() as u8);
53                        }
54                        return Ok(Bytes::from(bytes));
55                    }
56                }
57                Ok(Bytes::from(serde_json::to_string(&val)?))
58            }
59        }
60    }
61}
62
63impl InMemoryBody {
64    pub fn is_empty(&self) -> bool {
65        match self {
66            InMemoryBody::Empty => true,
67            InMemoryBody::Bytes(b) => b.is_empty(),
68            InMemoryBody::Text(s) => s.is_empty(),
69            InMemoryBody::Json(_) => false,
70        }
71    }
72
73    pub fn text(&self) -> InMemoryResult<Cow<str>> {
74        match self {
75            InMemoryBody::Empty => Ok(Cow::Borrowed("")),
76            InMemoryBody::Json(value) => serde_json::to_string(&value).map(Cow::Owned).map_err(Into::into),
77            InMemoryBody::Bytes(items) => std::str::from_utf8(items).map(Cow::Borrowed).map_err(Into::into),
78            InMemoryBody::Text(s) => Ok(Cow::Borrowed(s.as_str())),
79        }
80    }
81
82    pub fn into_text(self) -> InMemoryResult<String> {
83        self.try_into()
84    }
85
86    pub fn json<T: DeserializeOwned>(self) -> serde_json::Result<T> {
87        match self {
88            InMemoryBody::Empty => Err(serde_json::Error::custom("Empty body")),
89            InMemoryBody::Bytes(b) => serde_json::from_slice(&b),
90            InMemoryBody::Text(t) => serde_json::from_str(&t),
91            InMemoryBody::Json(v) => serde_json::from_value(v),
92        }
93    }
94
95    pub fn bytes(self) -> InMemoryResult<Bytes> {
96        self.try_into()
97    }
98
99    pub fn sanitize(&mut self) {
100        if let InMemoryBody::Json(value) = self {
101            sanitize_value(value);
102        }
103    }
104}
105
106impl std::hash::Hash for InMemoryBody {
107    fn hash<H: Hasher>(&self, state: &mut H) {
108        match self {
109            InMemoryBody::Empty => {}
110            // InMemoryBody::Empty => state.write_u8(0),
111            InMemoryBody::Bytes(b) => {
112                // state.write_u8(1);
113                state.write(b.as_slice());
114            }
115            InMemoryBody::Text(s) => {
116                // state.write_u8(2);
117                state.write(s.as_bytes());
118            }
119            InMemoryBody::Json(serde_json::Value::String(s)) => {
120                state.write(s.as_bytes());
121                // state.write_u8(3);
122                // state.write(v.to_string().as_bytes());
123            }
124            InMemoryBody::Json(v) => {
125                state.write(v.to_string().as_bytes());
126            }
127        }
128    }
129}
130
131impl Into<InMemoryBody> for String {
132    fn into(self) -> InMemoryBody {
133        InMemoryBody::Text(self)
134    }
135}
136
137impl Into<InMemoryBody> for Vec<u8> {
138    fn into(self) -> InMemoryBody {
139        InMemoryBody::Bytes(self)
140    }
141}