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
185impl Default for Body {
186 fn default() -> Body {
187 Body {
188 inner: Inner::Single(Single::Bytes(Bytes::new())),
189 }
190 }
191}
192
193#[allow(deprecated)]
197#[cfg(test)]
198mod tests {
199 use crate::Body;
200 use js_sys::Uint8Array;
201 use wasm_bindgen::prelude::*;
202 use wasm_bindgen_test::*;
203
204 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
205
206 #[wasm_bindgen]
207 extern "C" {
208 #[wasm_bindgen(js_namespace = console)]
211 fn log(s: String);
212 }
213
214 #[wasm_bindgen_test]
215 async fn test_body() {
216 let body = Body::from("TEST");
217 assert_eq!([84, 69, 83, 84], body.as_bytes().unwrap());
218 }
219
220 #[wasm_bindgen_test]
221 async fn test_body_js_static_str() {
222 let body_value = "TEST";
223 let body = Body::from(body_value);
224
225 let mut init = web_sys::RequestInit::new();
226 init.method("POST");
227 init.body(Some(
228 body.to_js_value()
229 .expect("could not convert body to JsValue")
230 .as_ref(),
231 ));
232
233 let js_req = web_sys::Request::new_with_str_and_init("", &init)
234 .expect("could not create JS request");
235 let text_promise = js_req.text().expect("could not get text promise");
236 let text = crate::wasm::promise::<JsValue>(text_promise)
237 .await
238 .expect("could not get request body as text");
239
240 assert_eq!(text.as_string().expect("text is not a string"), body_value);
241 }
242 #[wasm_bindgen_test]
243 async fn test_body_js_string() {
244 let body_value = "TEST".to_string();
245 let body = Body::from(body_value.clone());
246
247 let mut init = web_sys::RequestInit::new();
248 init.method("POST");
249 init.body(Some(
250 body.to_js_value()
251 .expect("could not convert body to JsValue")
252 .as_ref(),
253 ));
254
255 let js_req = web_sys::Request::new_with_str_and_init("", &init)
256 .expect("could not create JS request");
257 let text_promise = js_req.text().expect("could not get text promise");
258 let text = crate::wasm::promise::<JsValue>(text_promise)
259 .await
260 .expect("could not get request body as text");
261
262 assert_eq!(text.as_string().expect("text is not a string"), body_value);
263 }
264
265 #[wasm_bindgen_test]
266 async fn test_body_js_static_u8_slice() {
267 let body_value: &'static [u8] = b"\x00\x42";
268 let body = Body::from(body_value);
269
270 let mut init = web_sys::RequestInit::new();
271 init.method("POST");
272 init.body(Some(
273 body.to_js_value()
274 .expect("could not convert body to JsValue")
275 .as_ref(),
276 ));
277
278 let js_req = web_sys::Request::new_with_str_and_init("", &init)
279 .expect("could not create JS request");
280
281 let array_buffer_promise = js_req
282 .array_buffer()
283 .expect("could not get array_buffer promise");
284 let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
285 .await
286 .expect("could not get request body as array buffer");
287
288 let v = Uint8Array::new(&array_buffer).to_vec();
289
290 assert_eq!(v, body_value);
291 }
292
293 #[wasm_bindgen_test]
294 async fn test_body_js_vec_u8() {
295 let body_value = vec![0u8, 42];
296 let body = Body::from(body_value.clone());
297
298 let mut init = web_sys::RequestInit::new();
299 init.method("POST");
300 init.body(Some(
301 body.to_js_value()
302 .expect("could not convert body to JsValue")
303 .as_ref(),
304 ));
305
306 let js_req = web_sys::Request::new_with_str_and_init("", &init)
307 .expect("could not create JS request");
308
309 let array_buffer_promise = js_req
310 .array_buffer()
311 .expect("could not get array_buffer promise");
312 let array_buffer = crate::wasm::promise::<JsValue>(array_buffer_promise)
313 .await
314 .expect("could not get request body as array buffer");
315
316 let v = Uint8Array::new(&array_buffer).to_vec();
317
318 assert_eq!(v, body_value);
319 }
320}