use crate::callback::CallbackArg;
use crate::error::ErrorString;
use futures::Future;
use serde::{Deserialize, Serialize};
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::{future_to_promise, JsFuture};
pub struct FetchBuilder {
method: String,
url: String,
content_type: Option<String>,
accept_type: Option<String>,
request_init: web_sys::RequestInit,
}
impl FetchBuilder {
pub fn new(method: &str, url: &str) -> Self {
Self {
method: method.to_string(),
content_type: None,
accept_type: None,
url: url.to_string(),
request_init: web_sys::RequestInit::new(),
}
}
fn init_fetch(self) -> impl Future<Item = web_sys::Response, Error = JsValue> + 'static {
let FetchBuilder {
method,
url,
content_type,
accept_type,
mut request_init,
} = self;
request_init.method(&method);
let req = web_sys::Request::new_with_str_and_init(&url, &request_init)
.expect("new web_sys::Request");
if let Some(content_type) = content_type {
req.headers()
.set("Content-Type", &content_type)
.expect("set Content-Type");
}
if let Some(accept_type) = accept_type {
req.headers()
.set("Accept", &accept_type)
.expect("set Accept");
}
let win = web_sys::window().expect("web_sys::window()");
JsFuture::from(win.fetch_with_request(&req)).map(|res| {
res.dyn_into::<web_sys::Response>()
.expect("web_sys::Response")
})
}
#[cfg(feature = "fetch_json")]
pub fn body_json<S: Serialize>(&mut self, data: &S) -> Result<(), ErrorString> {
self.content_type = Some("application/json".to_string());
let data = ::serde_json::to_string(data)?;
let data = JsValue::from(data);
self.request_init.body(Some(&data));
Ok(())
}
#[cfg(feature = "fetch_json")]
pub fn fetch_json<R>(
mut self,
cb: CallbackArg<Result<R, ErrorString>>,
) -> Result<js_sys::Promise, ErrorString>
where
for<'a> R: Deserialize<'a> + 'static,
{
self.accept_type = Some("application/json".to_string());
let cb2 = cb.clone();
let rs = self
.init_fetch()
.and_then(|res| res.text())
.and_then(JsFuture::from)
.and_then(move |text| {
match text.as_string() {
Some(text) => match ::serde_json::from_str(&text) {
Ok(r) => cb(Ok(r)),
Err(e) => cb(Err(ErrorString::from(e))),
},
None => cb(Err(ErrorString::from("Response value is not a text?"))),
}
futures::future::ok(JsValue::null())
})
.or_else(move |e| {
cb2(Err(ErrorString::new(
e.as_string()
.unwrap_or("No message for the error".to_string()),
)));
futures::future::ok::<JsValue, JsValue>(JsValue::null())
});
Ok(future_to_promise(rs))
}
}