1#[cfg(feature = "multipart")]
2use super::multipart::Form;
3use bytes::Bytes;
5use js_sys::Uint8Array;
6use std::{borrow::Cow, fmt};
7use wasm_bindgen::JsValue;
8
9pub struct Body {
17 inner: Inner,
18}
19
20enum Inner {
21 Single(Single),
22 #[cfg(feature = "multipart")]
24 MultipartForm(Form),
25}
26
27#[derive(Clone)]
28pub(crate) enum Single {
29 Bytes(Bytes),
30 Text(Cow<'static, str>),
31}
32
33impl Single {
34 fn as_bytes(&self) -> &[u8] {
35 match self {
36 Single::Bytes(bytes) => bytes.as_ref(),
37 Single::Text(text) => text.as_bytes(),
38 }
39 }
40
41 pub(crate) fn to_js_value(&self) -> JsValue {
42 match self {
43 Single::Bytes(bytes) => {
44 let body_bytes: &[u8] = bytes.as_ref();
45 let body_uint8_array: Uint8Array = body_bytes.into();
46 let js_value: &JsValue = body_uint8_array.as_ref();
47 js_value.to_owned()
48 }
49 Single::Text(text) => JsValue::from_str(text),
50 }
51 }
52
53 fn is_empty(&self) -> bool {
54 match self {
55 Single::Bytes(bytes) => bytes.is_empty(),
56 Single::Text(text) => text.is_empty(),
57 }
58 }
59}
60
61impl Body {
62 #[inline]
66 pub fn as_bytes(&self) -> Option<&[u8]> {
67 match &self.inner {
68 Inner::Single(single) => Some(single.as_bytes()),
69 #[cfg(feature = "multipart")]
70 Inner::MultipartForm(_) => None,
71 }
72 }
73
74 pub(crate) fn to_js_value(&self) -> crate::Result<JsValue> {
75 match &self.inner {
76 Inner::Single(single) => Ok(single.to_js_value()),
77 #[cfg(feature = "multipart")]
78 Inner::MultipartForm(form) => {
79 let form_data = form.to_form_data()?;
80 let js_value: &JsValue = form_data.as_ref();
81 Ok(js_value.to_owned())
82 }
83 }
84 }
85
86 #[cfg(feature = "multipart")]
87 pub(crate) fn as_single(&self) -> Option<&Single> {
88 match &self.inner {
89 Inner::Single(single) => Some(single),
90 Inner::MultipartForm(_) => None,
91 }
92 }
93
94 #[inline]
95 #[cfg(feature = "multipart")]
96 pub(crate) fn from_form(f: Form) -> Body {
97 Self {
98 inner: Inner::MultipartForm(f),
99 }
100 }
101
102 #[cfg(feature = "multipart")]
104 pub(crate) fn into_part(self) -> Body {
105 match self.inner {
106 Inner::Single(single) => Self {
107 inner: Inner::Single(single),
108 },
109 Inner::MultipartForm(form) => Self {
110 inner: Inner::MultipartForm(form),
111 },
112 }
113 }
114
115 pub(crate) fn is_empty(&self) -> bool {
116 match &self.inner {
117 Inner::Single(single) => single.is_empty(),
118 #[cfg(feature = "multipart")]
119 Inner::MultipartForm(form) => form.is_empty(),
120 }
121 }
122
123 pub(crate) fn try_clone(&self) -> Option<Body> {
124 match &self.inner {
125 Inner::Single(single) => Some(Self {
126 inner: Inner::Single(single.clone()),
127 }),
128 #[cfg(feature = "multipart")]
129 Inner::MultipartForm(_) => None,
130 }
131 }
132}
133
134impl From<Bytes> for Body {
135 #[inline]
136 fn from(bytes: Bytes) -> Body {
137 Body {
138 inner: Inner::Single(Single::Bytes(bytes)),
139 }
140 }
141}
142
143impl From<Vec<u8>> for Body {
144 #[inline]
145 fn from(vec: Vec<u8>) -> Body {
146 Body {
147 inner: Inner::Single(Single::Bytes(vec.into())),
148 }
149 }
150}
151
152impl From<&'static [u8]> for Body {
153 #[inline]
154 fn from(s: &'static [u8]) -> Body {
155 Body {
156 inner: Inner::Single(Single::Bytes(Bytes::from_static(s))),
157 }
158 }
159}
160
161impl From<String> for Body {
162 #[inline]
163 fn from(s: String) -> Body {
164 Body {
165 inner: Inner::Single(Single::Text(s.into())),
166 }
167 }
168}
169
170impl From<&'static str> for Body {
171 #[inline]
172 fn from(s: &'static str) -> Body {
173 Body {
174 inner: Inner::Single(Single::Text(s.into())),
175 }
176 }
177}
178
179impl fmt::Debug for Body {
180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181 f.debug_struct("Body").finish()
182 }
183}
184
185#[allow(deprecated)]
189#[cfg(test)]
190mod tests {
191 use crate::Body;
192 use js_sys::Uint8Array;
193 use wasm_bindgen::prelude::*;
194 use wasm_bindgen_test::*;
195
196 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
197
198 #[wasm_bindgen]
199 extern "C" {
200 #[wasm_bindgen(js_namespace = console)]
203 fn log(s: String);
204 }
205
206 #[wasm_bindgen_test]
207 async fn test_body() {
208 let body = Body::from("TEST");
209 assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
210 }
211
212 #[wasm_bindgen_test]
213 async fn test_body_js_static_str() {
214 let body_value = "TEST";
215 let body = Body::from(body_value);
216
217 let mut init = web_sys::RequestInit::new();
218 init.method("POST");
219 init.body(Some(
220 body.to_js_value()
221 .expect("could not convert body to JsValue")
222 .as_ref(),
223 ));
224
225 let js_req = web_sys::Request::new_with_str_and_init("", &init)
226 .expect("could not create JS request");
227 let text_promise = js_req.text().expect("could not get text promise");
228 let text = crate::wasm::promise::<JsValue>(text_promise)
229 .await
230 .expect("could not get request body as text");
231
232 assert_eq!(text.as_string().expect("text is not a string"), body_value);
233 }
234 #[wasm_bindgen_test]
235 async fn test_body_js_string() {
236 let body_value = "TEST".to_string();
237 let body = Body::from(body_value.clone());
238
239 let mut init = web_sys::RequestInit::new();
240 init.method("POST");
241 init.body(Some(
242 body.to_js_value()
243 .expect("could not convert body to JsValue")
244 .as_ref(),
245 ));
246
247 let js_req = web_sys::Request::new_with_str_and_init("", &init)
248 .expect("could not create JS request");
249 let text_promise = js_req.text().expect("could not get text promise");
250 let text = crate::wasm::promise::<JsValue>(text_promise)
251 .await
252 .expect("could not get request body as text");
253
254 assert_eq!(text.as_string().expect("text is not a string"), body_value);
255 }
256
257 #[wasm_bindgen_test]
258 async fn test_body_js_static_u8_slice() {
259 let body_value: &'static [u8] = b"\x00\x42";
260 let body = Body::from(body_value);
261
262 let mut init = web_sys::RequestInit::new();
263 init.method("POST");
264 init.body(Some(
265 body.to_js_value()
266 .expect("could not convert body to JsValue")
267 .as_ref(),
268 ));
269
270 let js_req = web_sys::Request::new_with_str_and_init("", &init)
271 .expect("could not create JS request");
272
273 let array_buffer_promise = js_req
274 .array_buffer()
275 .expect("could not get array_buffer promise");
276 let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
277 .await
278 .expect("could not get request body as array buffer");
279
280 let v = Uint8Array::new(&array_buffer).to_vec();
281
282 assert_eq!(v, body_value);
283 }
284
285 #[wasm_bindgen_test]
286 async fn test_body_js_vec_u8() {
287 let body_value = vec![0u8, 42];
288 let body = Body::from(body_value.clone());
289
290 let mut init = web_sys::RequestInit::new();
291 init.method("POST");
292 init.body(Some(
293 body.to_js_value()
294 .expect("could not convert body to JsValue")
295 .as_ref(),
296 ));
297
298 let js_req = web_sys::Request::new_with_str_and_init("", &init)
299 .expect("could not create JS request");
300
301 let array_buffer_promise = js_req
302 .array_buffer()
303 .expect("could not get array_buffer promise");
304 let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
305 .await
306 .expect("could not get request body as array buffer");
307
308 let v = Uint8Array::new(&array_buffer).to_vec();
309
310 assert_eq!(v, body_value);
311 }
312}