use std::collections::HashMap;
use stdweb::Value;
use stdweb::unstable::{TryFrom, TryInto};
use yew::services::Task;
use yew::callback::Callback;
use http::{HeaderMap, Method, StatusCode, Uri};
use http;
pub struct FetchTask(Option<Value>);
#[derive(Default)]
pub struct FetchService {}
impl FetchTask {
pub fn new(
request: http::Request<Value>,
yew_callback: Callback<http::Response<String>>,
) -> FetchTask
{
let (parts, body): (_, Value) = request.into_parts();
let header_map: HashMap<&str, &str> = parts
.headers
.iter()
.map(|(k, v)| {
let v = expect!(
v.to_str(),
"Unparsable request header {}: {:?}", k.as_str(), v
);
(
k.as_str(),
v,
)
})
.collect();
let uri = format!("{}", parts.uri);
let js_callback = move |success: bool, response: Value, recv_body: String| -> () {
let mut response_builder = http::Response::builder();
let status = u16::try_from(js!{
return @{&response}.status;
});
if let Ok(code) = status {
response_builder.status(code);
}
let headers: HashMap<String, String> = HashMap::try_from(js!{
var map = {};
@{&response}.headers.forEach(function(value, key) {
map[key] = value;
});
return map;
}).unwrap_or_default();
for (key, values) in &headers {
response_builder.header(key.as_str(), values.as_str());
}
let response = expect!(response_builder.body(recv_body));
yew_callback.emit(response);
};
let handle = js! {
var data = {
method: @{parts.method.as_str()},
body: @{body},
headers: @{header_map},
};
var request = new Request(@{uri}, data);
var callback = @{js_callback};
var handle = {
active: true,
callback: callback,
};
fetch(request).then(function(response) {
response.text().then(function(data) {
if (handle.active == true) {
handle.active = false;
callback(true, response, data);
callback.drop();
}
}).catch(function(err) {
if (handle.active == true) {
handle.active = false;
callback(false, response, data);
callback.drop();
}
});
});
return handle;
};
FetchTask(Some(handle))
}
}
impl Task for FetchTask {
fn is_active(&self) -> bool {
if let Some(ref task) = self.0 {
let result = js! {
var the_task = @{task};
return the_task.active;
};
result.try_into().unwrap_or(false)
} else {
false
}
}
fn cancel(&mut self) {
let handle = self.0
.take()
.expect("tried to cancel request fetching twice");
js! { @(no_return)
var handle = @{handle};
handle.active = false;
handle.callback.drop();
}
}
}
impl Drop for FetchTask {
fn drop(&mut self) {
if self.is_active() {
self.cancel();
}
}
}