use std::fmt;
use bytes::Bytes;
use http::{HeaderMap, StatusCode};
use js_sys::Uint8Array;
use url::Url;
use crate::wasm::AbortGuard;
#[cfg(feature = "stream")]
use wasm_bindgen::JsCast;
#[cfg(feature = "stream")]
use futures_util::stream::StreamExt;
#[cfg(feature = "json")]
use serde::de::DeserializeOwned;
pub struct Response {
http: http::Response<web_sys::Response>,
_abort: AbortGuard,
url: Box<Url>,
}
impl Response {
pub(super) fn new(
res: http::Response<web_sys::Response>,
url: Url,
abort: AbortGuard,
) -> Response {
Response {
http: res,
url: Box::new(url),
_abort: abort,
}
}
#[inline]
pub fn status(&self) -> StatusCode {
self.http.status()
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
self.http.headers()
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
self.http.headers_mut()
}
pub fn content_length(&self) -> Option<u64> {
self.headers()
.get(http::header::CONTENT_LENGTH)?
.to_str()
.ok()?
.parse()
.ok()
}
#[inline]
pub fn url(&self) -> &Url {
&self.url
}
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
pub async fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
let full = self.bytes().await?;
serde_json::from_slice(&full).map_err(crate::error::decode)
}
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"))
}
}
pub async fn bytes(self) -> crate::Result<Bytes> {
let p = self
.http
.body()
.array_buffer()
.map_err(crate::error::wasm)
.map_err(crate::error::decode)?;
let buf_js = super::promise::<wasm_bindgen::JsValue>(p)
.await
.map_err(crate::error::decode)?;
let buffer = Uint8Array::new(&buf_js);
let mut bytes = vec![0; buffer.length() as usize];
buffer.copy_to(&mut bytes);
Ok(bytes.into())
}
#[cfg(feature = "stream")]
pub fn bytes_stream(self) -> impl futures_core::Stream<Item = crate::Result<Bytes>> {
let web_response = self.http.into_body();
let abort = self._abort;
let body = web_response
.body()
.expect("could not create wasm byte stream");
let body = wasm_streams::ReadableStream::from_raw(body.unchecked_into());
Box::pin(body.into_stream().map(move |buf_js| {
let _abort = &abort;
let buffer = Uint8Array::new(
&buf_js
.map_err(crate::error::wasm)
.map_err(crate::error::decode)?,
);
let mut bytes = vec![0; buffer.length() as usize];
buffer.copy_to(&mut bytes);
Ok(bytes.into())
}))
}
pub fn error_for_status(self) -> crate::Result<Self> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(crate::error::status_code(*self.url, status))
} else {
Ok(self)
}
}
pub fn error_for_status_ref(&self) -> crate::Result<&Self> {
let status = self.status();
if status.is_client_error() || status.is_server_error() {
Err(crate::error::status_code(*self.url.clone(), status))
} else {
Ok(self)
}
}
}
impl fmt::Debug for Response {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Response")
.field("status", &self.status())
.field("headers", self.headers())
.finish()
}
}