use std::collections::HashMap;
use js_sys::Uint8Array;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
use web_sys::{Blob, ErrorEvent, FormData, ProgressEvent, XmlHttpRequest, XmlHttpRequestResponseType};
use crate::log;
enum PostRequestContent {
Form(FormData),
Blob(Blob),
}
pub struct PostRequest {
upload_onabort: Option<JsValue>,
upload_onerror: Option<JsValue>,
upload_onload: Option<JsValue>,
upload_onloadend: Option<JsValue>,
upload_onloadstart: Option<JsValue>,
upload_on_progress: Option<JsValue>,
upload_ontimeout: Option<JsValue>,
request_onabort: Option<JsValue>,
request_onerror: Option<JsValue>,
request_onload: Option<JsValue>,
request_onloadend: Option<JsValue>,
request_onloadstart: Option<JsValue>,
request_on_progress: Option<JsValue>,
request_ontimeout: Option<JsValue>,
_async: bool,
content: Option<PostRequestContent>,
headers: HashMap<String, String>,
}
impl PostRequest {
pub fn new_from_default()->Self {
PostRequest {
..Default::default()
}
}
pub fn new_from_blob(blob: Vec<u8>, mime_type: &str) -> Self {
let uint8arr: Uint8Array = blob.as_slice().into();
let blob = Blob::new_with_u8_array_sequence_and_options(
&uint8arr,
web_sys::BlobPropertyBag::new().type_(mime_type),
)
.unwrap();
PostRequest {
content: Some(PostRequestContent::Blob(blob)),
..Default::default()
}
}
pub fn set_upload_onabort(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.upload_onabort = callback;
}
pub fn set_upload_onerror(&mut self, closure: Option<Box<dyn Fn(ErrorEvent)>>) {
let callback = closure.map(|c| Closure::<dyn Fn(ErrorEvent)>::wrap(c).into_js_value());
self.upload_onerror = callback;
}
pub fn set_upload_onload(&mut self, closure: Option<Box<dyn Fn(ProgressEvent)>>) {
let callback = closure.map(|c| Closure::<dyn Fn(ProgressEvent)>::wrap(c).into_js_value());
self.upload_onload = callback;
}
pub fn set_upload_onloadstart(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.upload_onloadstart = callback;
}
pub fn set_upload_onloadend(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.upload_onloadend = callback;
}
pub fn set_upload_onprogress(&mut self, closure: Option<Box<dyn Fn(ProgressEvent)>>) {
let callback = closure.map(|c| Closure::<dyn Fn(ProgressEvent)>::wrap(c).into_js_value());
self.upload_on_progress = callback;
}
pub fn set_request_onabort(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.request_onabort = callback;
}
pub fn set_request_onerror(&mut self, fun:Option<JsValue>) {
self.request_onerror = fun;
}
pub fn set_request_onload(&mut self, fun:Option<JsValue>) {
self.request_onload = fun;
}
pub fn set_request_onloadstart(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.request_onloadstart = callback;
}
pub fn set_request_onloadend(&mut self, closure: Option<Box<dyn Fn()>>) {
let callback = closure.map(|c| Closure::<dyn Fn()>::wrap(c).into_js_value());
self.request_onloadstart = callback;
}
pub fn set_request_onprogress(&mut self, closure: Option<Box<dyn Fn(ProgressEvent)>>) {
let callback = closure.map(|c| Closure::<dyn Fn(ProgressEvent)>::wrap(c).into_js_value());
self.request_on_progress = callback;
}
pub fn set_async(&mut self, _async: bool) {
self._async = _async;
}
pub fn send(self, url: &str) -> Result<SendRequest, JsValue> {
let request = self.get_request(url);
self.set_request_callbacks(&request);
self.set_headers(&request)?;
let result: SendRequest = self.send_request(request)?.into();
Ok(result)
}
pub fn set_header(&mut self, header: String, value: String) {
self.headers.insert(header, value);
}
fn send_request(self, request: XmlHttpRequest) -> Result<XmlHttpRequest, JsValue> {
let result = match &self.content {
Some(content) => match content {
PostRequestContent::Form(form) => request.send_with_opt_form_data(Some(form)),
PostRequestContent::Blob(blob) => request.send_with_opt_blob(Some(blob)),
},
None => request.send(),
};
match result {
Ok(_) => Ok(request),
Err(error) => Err(error),
}
}
fn get_request(&self, url: &str) -> XmlHttpRequest {
let request = XmlHttpRequest::new().expect("Could not create request!");
request
.open_with_async("GET", url, self._async)
.expect("Could not open request");
request
}
fn set_request_callbacks(&self, request: &XmlHttpRequest) {
log!("set_request_callbacks");
if let Some(callback) = &self.request_onabort {
request.set_onabort(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_onerror {
request.set_onerror(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_onload {
log!("Request onload{:?}", request);
request.set_onload(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_onloadend {
request.set_onloadend(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_onloadstart {
request.set_onloadstart(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_on_progress {
request.set_onprogress(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.request_ontimeout {
request.set_ontimeout(Some(callback.as_ref().unchecked_ref()));
}
}
fn set_upload_callbacks(&self, request: &XmlHttpRequest) {
let upload = request
.upload()
.expect("Could not fetch upload field of xmlhttprequest");
if let Some(callback) = &self.upload_onabort {
upload.set_onabort(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_onerror {
upload.set_onerror(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_onload {
upload.set_onload(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_onloadend {
upload.set_onloadend(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_onloadstart {
upload.set_onloadstart(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_on_progress {
upload.set_onprogress(Some(callback.as_ref().unchecked_ref()));
}
if let Some(callback) = &self.upload_ontimeout {
upload.set_ontimeout(Some(callback.as_ref().unchecked_ref()));
}
}
fn set_headers(&self, request: &XmlHttpRequest) -> Result<(), JsValue> {
for (key, value) in &self.headers {
request.set_request_header(key, value)?;
}
Ok(())
}
}
impl Default for PostRequest {
fn default() -> Self {
Self {
_async: true,
content: None,
upload_onabort: None,
upload_onerror: None,
upload_onload: None,
upload_onloadend: None,
upload_onloadstart: None,
upload_on_progress: None,
upload_ontimeout: None,
request_onload: None,
request_onabort: None,
request_onerror: None,
request_onloadend: None,
request_onloadstart: None,
request_on_progress: None,
request_ontimeout: None,
headers: Default::default(),
}
}
}
pub struct SendRequest {
pub request: XmlHttpRequest,
}
impl SendRequest {
pub fn abort(self) {
self.request
.abort()
.expect("tried to abort request which could not be aborted");
}
pub fn status(&self) -> u16 {
self.request
.status()
.expect("Could not fetch request status")
}
pub fn ready_state(&self) -> u16 {
self.request.ready_state()
}
}
impl From<XmlHttpRequest> for SendRequest {
fn from(request: XmlHttpRequest) -> Self {
Self { request }
}
}