Introduce unstable, incomplete WASM support
This commit is contained in:
		
							
								
								
									
										3
									
								
								src/wasm/body.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/wasm/body.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| /// dox | ||||
| #[derive(Debug)] | ||||
| pub struct Body(()); | ||||
							
								
								
									
										162
									
								
								src/wasm/client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/wasm/client.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| use std::future::Future; | ||||
| use http::Method; | ||||
| use wasm_bindgen::UnwrapThrowExt as _; | ||||
|  | ||||
| use crate::IntoUrl; | ||||
| use super::{Request, RequestBuilder, Response}; | ||||
|  | ||||
| /// dox | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct Client(()); | ||||
|  | ||||
| /// dox | ||||
| #[derive(Debug)] | ||||
| pub struct ClientBuilder(()); | ||||
|  | ||||
| impl Client { | ||||
|     /// dox | ||||
|     pub fn new() -> Self { | ||||
|         Client::builder().build().unwrap_throw() | ||||
|     } | ||||
|  | ||||
|     /// dox | ||||
|     pub fn builder() -> ClientBuilder { | ||||
|         ClientBuilder::new() | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `GET` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::GET, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `POST` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::POST, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PUT` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::PUT, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PATCH` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::PATCH, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `DELETE` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::DELETE, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `HEAD` request to a URL. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::HEAD, url) | ||||
|     } | ||||
|  | ||||
|     /// Start building a `Request` with the `Method` and `Url`. | ||||
|     /// | ||||
|     /// Returns a `RequestBuilder`, which will allow setting headers and | ||||
|     /// request body before sending. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { | ||||
|         let req = url.into_url().map(move |url| Request::new(method, url)); | ||||
|         RequestBuilder::new(self.clone(), req) | ||||
|     } | ||||
|  | ||||
|     pub(super) fn execute_request(&self, req: Request) -> impl Future<Output = crate::Result<Response>> { | ||||
|         fetch(req) | ||||
|     } | ||||
| } | ||||
|  | ||||
| async fn fetch(req: Request) -> crate::Result<Response> { | ||||
|     // Build the js Request | ||||
|     let mut init = web_sys::RequestInit::new(); | ||||
|     init.method(req.method().as_str()); | ||||
|  | ||||
|     let js_headers = web_sys::Headers::new() | ||||
|         .map_err(crate::error::wasm) | ||||
|         .map_err(crate::error::builder)?; | ||||
|  | ||||
|     for (name, value) in req.headers() { | ||||
|         js_headers | ||||
|             .append(name.as_str(), value.to_str().map_err(crate::error::builder)?) | ||||
|             .map_err(crate::error::wasm) | ||||
|             .map_err(crate::error::builder)?; | ||||
|     } | ||||
|     init.headers(&js_headers.into()); | ||||
|  | ||||
|     let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init) | ||||
|         .map_err(crate::error::wasm) | ||||
|         .map_err(crate::error::builder)?; | ||||
|  | ||||
|     // Await the fetch() promise | ||||
|     let p = web_sys::window() | ||||
|         .expect("window should exist") | ||||
|         .fetch_with_request(&js_req); | ||||
|     let js_resp = super::promise::<web_sys::Response>(p) | ||||
|         .await | ||||
|         .map_err(crate::error::request)?; | ||||
|  | ||||
|     // Convert from the js Response | ||||
|     let mut resp = http::Response::builder(); | ||||
|     resp.status(js_resp.status()); | ||||
|  | ||||
|     // TODO: translate js_resp.headers() | ||||
|     /* | ||||
|     let js_headers = js_resp.headers(); | ||||
|     let js_iter = js_sys::try_iter(&js_headers) | ||||
|         .expect_throw("headers try_iter") | ||||
|         .expect_throw("headers have an iterator"); | ||||
|  | ||||
|     for item in js_iter { | ||||
|         let item = item.expect_throw("headers iterator doesn't throw"); | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     resp.body(js_resp) | ||||
|         .map(Response::new) | ||||
|         .map_err(crate::error::request) | ||||
| } | ||||
|  | ||||
| // ===== impl ClientBuilder ===== | ||||
|  | ||||
| impl ClientBuilder { | ||||
|     /// dox | ||||
|     pub fn new() -> Self { | ||||
|         ClientBuilder(()) | ||||
|     } | ||||
|  | ||||
|     /// dox | ||||
|     pub fn build(self) -> Result<Client, crate::Error> { | ||||
|         Ok(Client(())) | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/wasm/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/wasm/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| use wasm_bindgen::JsCast; | ||||
|  | ||||
| mod body; | ||||
| mod client; | ||||
| mod request; | ||||
| mod response; | ||||
|  | ||||
| pub use self::body::Body; | ||||
| pub use self::client::{Client, ClientBuilder}; | ||||
| pub use self::request::{Request, RequestBuilder}; | ||||
| pub use self::response::Response; | ||||
|  | ||||
|  | ||||
| async fn promise<T>(promise: js_sys::Promise) -> Result<T, crate::error::BoxError> | ||||
| where | ||||
|     T: JsCast, | ||||
| { | ||||
|     use wasm_bindgen_futures::futures_0_3::JsFuture; | ||||
|  | ||||
|     let js_val = JsFuture::from(promise) | ||||
|         .await | ||||
|         .map_err(crate::error::wasm)?; | ||||
|  | ||||
|     js_val | ||||
|         .dyn_into::<T>() | ||||
|         .map_err(|_js_val| { | ||||
|             "promise resolved to unexpected type".into() | ||||
|         }) | ||||
| } | ||||
							
								
								
									
										144
									
								
								src/wasm/request.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/wasm/request.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use http::{Method, HeaderMap}; | ||||
| use url::Url; | ||||
|  | ||||
| use super::{Body, Client, Response}; | ||||
|  | ||||
| /// A request which can be executed with `Client::execute()`. | ||||
| pub struct Request { | ||||
|     method: Method, | ||||
|     url: Url, | ||||
|     headers: HeaderMap, | ||||
|     body: Option<Body>, | ||||
| } | ||||
|  | ||||
| /// A builder to construct the properties of a `Request`. | ||||
| pub struct RequestBuilder { | ||||
|     client: Client, | ||||
|     request: crate::Result<Request>, | ||||
| } | ||||
|  | ||||
| impl Request { | ||||
|     pub(super) fn new(method: Method, url: Url) -> Self { | ||||
|         Request { | ||||
|             method, | ||||
|             url, | ||||
|             headers: HeaderMap::new(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get the method. | ||||
|     #[inline] | ||||
|     pub fn method(&self) -> &Method { | ||||
|         &self.method | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the method. | ||||
|     #[inline] | ||||
|     pub fn method_mut(&mut self) -> &mut Method { | ||||
|         &mut self.method | ||||
|     } | ||||
|  | ||||
|     /// Get the url. | ||||
|     #[inline] | ||||
|     pub fn url(&self) -> &Url { | ||||
|         &self.url | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the url. | ||||
|     #[inline] | ||||
|     pub fn url_mut(&mut self) -> &mut Url { | ||||
|         &mut self.url | ||||
|     } | ||||
|  | ||||
|     /// Get the headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         &self.headers | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut HeaderMap { | ||||
|         &mut self.headers | ||||
|     } | ||||
|  | ||||
|     /// Get the body. | ||||
|     #[inline] | ||||
|     pub fn body(&self) -> Option<&Body> { | ||||
|         self.body.as_ref() | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the body. | ||||
|     #[inline] | ||||
|     pub fn body_mut(&mut self) -> &mut Option<Body> { | ||||
|         &mut self.body | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl RequestBuilder { | ||||
|     pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder { | ||||
|         RequestBuilder { client, request } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// Set the request body. | ||||
|     pub fn body<T: Into<Body>>(mut self, body: T) -> RequestBuilder { | ||||
|         if let Ok(ref mut req) = self.request { | ||||
|             req.body = Some(body.into()); | ||||
|         } | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Constructs the Request and sends it to the target URL, returning a | ||||
|     /// future Response. | ||||
|     /// | ||||
|     /// # Errors | ||||
|     /// | ||||
|     /// This method fails if there was an error while sending request. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
|     /// ```no_run | ||||
|     /// # use reqwest::Error; | ||||
|     /// # | ||||
|     /// # async fn run() -> Result<(), Error> { | ||||
|     /// let response = reqwest::Client::new() | ||||
|     ///     .get("https://hyper.rs") | ||||
|     ///     .send() | ||||
|     ///     .await?; | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
|     /// ``` | ||||
|     pub async fn send(self) -> crate::Result<Response> { | ||||
|         let req = self.request?; | ||||
|         self.client.execute_request(req).await | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Request { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         fmt_request_fields(&mut f.debug_struct("Request"), self).finish() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for RequestBuilder { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let mut builder = f.debug_struct("RequestBuilder"); | ||||
|         match self.request { | ||||
|             Ok(ref req) => fmt_request_fields(&mut builder, req).finish(), | ||||
|             Err(ref err) => builder.field("error", err).finish(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn fmt_request_fields<'a, 'b>( | ||||
|     f: &'a mut fmt::DebugStruct<'a, 'b>, | ||||
|     req: &Request, | ||||
| ) -> &'a mut fmt::DebugStruct<'a, 'b> { | ||||
|     f.field("method", &req.method) | ||||
|         .field("url", &req.url) | ||||
|         .field("headers", &req.headers) | ||||
| } | ||||
							
								
								
									
										73
									
								
								src/wasm/response.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/wasm/response.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use http::{HeaderMap, StatusCode}; | ||||
|  | ||||
| /// A Response to a submitted `Request`. | ||||
| pub struct Response { | ||||
|     http: http::Response<web_sys::Response>, | ||||
| } | ||||
|  | ||||
| impl Response { | ||||
|     pub(super) fn new( | ||||
|         res: http::Response<web_sys::Response>, | ||||
|         //url: Url, | ||||
|     ) -> Response { | ||||
|         Response { | ||||
|             http: res, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Get the `StatusCode` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn status(&self) -> StatusCode { | ||||
|         self.http.status() | ||||
|     } | ||||
|  | ||||
|     /// Get the `Headers` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         self.http.headers() | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the `Headers` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut HeaderMap { | ||||
|         self.http.headers_mut() | ||||
|     } | ||||
|  | ||||
|     /* It might not be possible to detect this in JS? | ||||
|     /// Get the HTTP `Version` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn version(&self) -> Version { | ||||
|         self.http.version() | ||||
|     } | ||||
|     */ | ||||
|  | ||||
|     // pub async fn json() | ||||
|  | ||||
|  | ||||
|     /// Get the response text. | ||||
|     pub async fn text(self) -> crate::Result<String> { | ||||
|         let p = self.http.body().text() | ||||
|             .map_err(crate::error::wasm) | ||||
|             .map_err(crate::error::decode)?; | ||||
|         let js_val = super::promise::<wasm_bindgen::JsValue>(p) | ||||
|             .await | ||||
|             .map_err(crate::error::decode)?; | ||||
|         if let Some(s) = js_val.as_string() { | ||||
|             Ok(s) | ||||
|         } else { | ||||
|             Err(crate::error::decode("response.text isn't string")) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Debug for Response { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Response") | ||||
|             //.field("url", self.url()) | ||||
|             .field("status", &self.status()) | ||||
|             .field("headers", self.headers()) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user