1use crate::errors::{Error, NativeError};
6use std::ops::Deref;
7use js_sys::ArrayBuffer;
9use wasm_bindgen::JsCast;
10use wasm_bindgen::prelude::*;
11use wasm_bindgen_futures::JsFuture;
12use web_sys::{ Request, AbortSignal,RequestInit, File, Blob};
13
14#[cfg(feature = "serde")]
15use serde::{Serialize, de::DeserializeOwned};
16use super::helpers::AbortController;
17
18pub struct Response {
22 response: web_sys::Response,
23 _abort: Option<AbortController>,
26}
27
28impl Deref for Response {
29 type Target = web_sys::Response;
30
31 fn deref(&self) -> &Self::Target {
32 &self.response
33 }
34}
35
36impl Response {
37 pub async fn text(self) -> Result<String, Error> {
38 JsFuture::from(
39 self.response
40 .text()
41 .map_err(|err| Error::from(err))?
42 ).await
43 .map_err(|err| Error::from(err))
44 .map(|value| value.as_string().unwrap_throw())
45 }
46
47 pub async fn json_raw(self) -> Result<JsValue, Error> {
48 JsFuture::from(
49 self.response
50 .json()
51 .map_err(|err| Error::from(err))?
52 ).await.map_err(|err| Error::from(err))
53 }
54
55 pub async fn array_buffer(self) -> Result<ArrayBuffer, Error> {
56 JsFuture::from(
57 self.response
58 .array_buffer()
59 .map_err(|err| Error::from(err))?
60 ).await
61 .map_err(|err| Error::from(err))
62 .map(|value| value.into())
63 }
64
65 #[cfg(feature = "serde")]
66 pub async fn json_from_obj<T: DeserializeOwned>(self) -> Result<T, Error> {
67 let data = self.json_raw().await?;
68
69 serde_wasm_bindgen::from_value(data).map_err(|err| Error::from(JsValue::from(err)))
70 }
71
72 #[cfg(feature = "serde")]
73 pub async fn json_from_str<T: DeserializeOwned>(self) -> Result<T, Error> {
74 let data = self.text().await?;
75
76 serde_json::from_str(&data).map_err(|err| Error::from(err))
77 }
78
79 #[cfg(feature = "audio")]
80 pub async fn audio(self, ctx: &web_sys::AudioContext) -> Result<web_sys::AudioBuffer, Error> {
81 let buffer = self.array_buffer().await?;
82
83 super::audio::audio_buffer(&buffer, &ctx).await
84 }
85
86 #[cfg(feature = "image")]
87 pub async fn image(self, mime_type:&str) -> Result<web_sys::HtmlImageElement, Error> {
88 let buffer= self.array_buffer().await?;
89
90 super::image::load_js_value(&buffer, mime_type).await
91 }
92}
93
94pub async fn fetch_req(req: &Request, abort_controller: Option<&AbortController>, init:&mut RequestInit) -> Result<Response, Error> {
103 let abort = match abort_controller {
106 Some(a) => {
107 init.signal(Some(&a.signal()));
108 None
109 },
110 None => {
111 let a = AbortController::new();
112 init.signal(Some(&a.signal()));
113 Some(a)
114 }
115 };
116
117 let future = web_sys::window()
118 .unwrap_throw()
119 .fetch_with_request_and_init(req, init);
120
121 JsFuture::from(future).await
122 .map(|response| {
123 let response = response.unchecked_into::<web_sys::Response>();
124 Response { response, _abort: abort }
125 })
126 .map_err(|err| err.into())
127
128}
129
130pub async fn fetch_url(url:&str) -> Result<Response, Error> {
131 fetch_url_abortable(url, None).await
132}
133pub async fn fetch_url_abortable(url:&str, abort_controller: Option<&AbortController>) -> Result<Response, Error> {
134 fetch_req(&Request::new_with_str(url)?, abort_controller, &mut RequestInit::new()).await
135}
136
137pub async fn fetch_with_headers<A: AsRef<str>, B: AsRef<str>>(url: &str, method:&str, include_credentials: bool, pairs: &[(A, B)]) -> Result<Response, Error> {
138 fetch_with_headers_abortable(url, method, include_credentials, None, pairs).await
139}
140
141pub async fn fetch_with_headers_abortable<A: AsRef<str>, B: AsRef<str>>(url: &str, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, pairs: &[(A, B)]) -> Result<Response, Error> {
142 let mut req_init = web_sys::RequestInit::new();
143 req_init.method(method);
144 if include_credentials {
145 req_init.credentials(web_sys::RequestCredentials::Include);
146 }
147
148
149 let req = web_sys::Request::new_with_str_and_init(url, &req_init)?;
150
151 let headers = req.headers();
152
153 for (name, value) in pairs.iter() {
154 headers.set(name.as_ref(), value.as_ref())?;
155 }
156
157 fetch_req(&req, abort_controller, &mut req_init).await
158}
159
160pub async fn fetch_upload_body_with_headers<A: AsRef<str>, B: AsRef<str>>(url: &str, body:&JsValue, method:&str, include_credentials: bool, pairs: &[(A, B)]) -> Result<Response, Error> {
161 fetch_upload_body_with_headers_abortable(url, body, method, include_credentials, None, pairs).await
162}
163
164pub async fn fetch_upload_body_with_headers_abortable<A: AsRef<str>, B: AsRef<str>>(url: &str, body:&JsValue, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, pairs: &[(A, B)]) -> Result<Response, Error> {
165 let mut req_init = web_sys::RequestInit::new();
166 req_init.method(method);
167 if include_credentials {
168 req_init.credentials(web_sys::RequestCredentials::Include);
169 }
170 req_init.body(Some(body));
171
172
173 let req = web_sys::Request::new_with_str_and_init(url, &req_init)?;
174
175 let headers = req.headers();
176
177 for (name, value) in pairs.iter() {
178 headers.set(name.as_ref(), value.as_ref())?;
179 }
180
181 fetch_req(&req, abort_controller, &mut req_init).await
182}
183pub async fn fetch_upload_body(url:&str, body:&JsValue, method:&str) -> Result<Response, Error> {
184 fetch_upload_body_abortable(url, body, method, None).await
185}
186
187pub async fn fetch_upload_body_abortable(url:&str, body:&JsValue, method:&str, abort_controller: Option<&AbortController>) -> Result<Response, Error> {
188 let mut req_init = web_sys::RequestInit::new();
189 req_init.method(method);
190 req_init.body(Some(body));
191
192 let req = web_sys::Request::new_with_str_and_init(url, &req_init)?;
193
194 fetch_req(&req, abort_controller, &mut req_init).await
195
196}
197
198pub async fn fetch_upload_blob_with_headers<A: AsRef<str>, B: AsRef<str>>(url: &str, blob:&Blob, method:&str, include_credentials: bool, pairs: &[(A, B)]) -> Result<Response, Error> {
199 fetch_upload_blob_with_headers_abortable(url, blob, method, include_credentials, None, pairs).await
200}
201
202pub async fn fetch_upload_blob_with_headers_abortable<A: AsRef<str>, B: AsRef<str>>(url: &str, blob:&Blob, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, pairs: &[(A, B)]) -> Result<Response, Error> {
203 fetch_upload_body_with_headers_abortable(url, blob, method, include_credentials, abort_controller, pairs).await
204}
205
206pub async fn fetch_upload_blob(url:&str, blob:&Blob, method:&str) -> Result<Response, Error> {
207 fetch_upload_blob_abortable(url, blob, method, None).await
208}
209
210pub async fn fetch_upload_blob_abortable(url:&str, blob:&Blob, method:&str, abort_controller: Option<&AbortController>) -> Result<Response, Error> {
211 fetch_upload_body_abortable(url, blob, method, abort_controller).await
212}
213
214pub async fn fetch_upload_file_with_headers<A: AsRef<str>, B: AsRef<str>>(url: &str, file:&File, method:&str, include_credentials: bool, pairs: &[(A, B)]) -> Result<Response, Error> {
215 fetch_upload_file_with_headers_abortable(url, file, method, include_credentials, None, pairs).await
216}
217
218pub async fn fetch_upload_file_with_headers_abortable<A: AsRef<str>, B: AsRef<str>>(url: &str, file:&File, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, pairs: &[(A, B)]) -> Result<Response, Error> {
219 fetch_upload_body_with_headers_abortable(url, file, method, include_credentials, abort_controller, pairs).await
220}
221
222pub async fn fetch_upload_file(url:&str, file:&File, method:&str) -> Result<Response, Error> {
223 fetch_upload_file_abortable(url, file, method, None).await
224}
225
226pub async fn fetch_upload_file_abortable(url:&str, file:&File, method:&str, abort_controller: Option<&AbortController>) -> Result<Response, Error> {
227 fetch_upload_body_abortable(url, file, method, abort_controller).await
228}
229
230#[cfg(feature = "serde_json")]
231pub async fn fetch_with_data(url: &str, method:&str, include_credentials: bool, data:Option<impl Serialize>) -> Result<Response, Error> {
232 fetch_with_data_abortable(url, method, include_credentials, None, data).await
233}
234
235#[cfg(feature = "serde_json")]
236pub async fn fetch_with_data_abortable(url: &str, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, data:Option<impl Serialize>) -> Result<Response, Error> {
237 let mut req_init = web_sys::RequestInit::new();
238 req_init.method(method);
239 if include_credentials {
240 req_init.credentials(web_sys::RequestCredentials::Include);
241 }
242
243
244 let req = match data {
245 None => web_sys::Request::new_with_str_and_init(url, &req_init)?,
246
247 Some(data) => {
248 let json_str = serde_json::to_string(&data).map_err(|err| JsValue::from_str(&err.to_string()))?;
249 req_init.body(Some(&JsValue::from_str(&json_str)));
251 let req = web_sys::Request::new_with_str_and_init(url, &req_init)?;
252 req.headers().set("Content-Type", "application/json")?;
253
254 req
255 }
256 };
257
258 fetch_req(&req, abort_controller, &mut req_init).await
259}
260
261
262#[cfg(feature = "serde_json")]
263pub async fn fetch_with_headers_and_data<A: AsRef<str>, B: AsRef<str>>(url: &str, method:&str, include_credentials: bool, pairs: &[(A, B)], data:Option<impl Serialize>) -> Result<Response, Error> {
264 fetch_with_headers_and_data_abortable(url, method, include_credentials, None, pairs, data).await
265}
266
267#[cfg(feature = "serde_json")]
268pub async fn fetch_with_headers_and_data_abortable<A: AsRef<str>, B: AsRef<str>>(url: &str, method:&str, include_credentials: bool, abort_controller: Option<&AbortController>, pairs: &[(A, B)], data:Option<impl Serialize>) -> Result<Response, Error> {
269 let mut req_init = web_sys::RequestInit::new();
270 req_init.method(method);
271 if include_credentials {
272 req_init.credentials(web_sys::RequestCredentials::Include);
273 }
274
275
276 let req = match data {
277 None => web_sys::Request::new_with_str_and_init(url, &req_init)?,
278
279 Some(data) => {
280 let json_str = serde_json::to_string(&data).map_err(|err| JsValue::from_str(&err.to_string()))?;
281 req_init.body(Some(&JsValue::from_str(&json_str)));
283 let req = web_sys::Request::new_with_str_and_init(url, &req_init)?;
284 req.headers().set("Content-Type", "application/json")?;
285
286 req
287 }
288 };
289
290 let headers = req.headers();
291
292 for (name, value) in pairs.iter() {
293 headers.set(name.as_ref(), value.as_ref())?;
294 }
295
296 fetch_req(&req, abort_controller, &mut req_init).await
297}