server_fn/response/
browser.rs1use super::ClientRes;
2use crate::{
3 error::{FromServerFnError, IntoAppError, ServerFnErrorErr},
4 redirect::REDIRECT_HEADER,
5};
6use bytes::Bytes;
7use futures::{Stream, StreamExt};
8pub use gloo_net::http::Response;
9use http::{HeaderMap, HeaderName, HeaderValue};
10use js_sys::Uint8Array;
11use send_wrapper::SendWrapper;
12use std::{future::Future, str::FromStr};
13use wasm_bindgen::JsCast;
14use wasm_streams::ReadableStream;
15
16pub struct BrowserResponse(pub(crate) SendWrapper<Response>);
18
19impl BrowserResponse {
20 pub fn generate_headers(&self) -> HeaderMap {
25 self.0
26 .headers()
27 .entries()
28 .filter_map(|(key, value)| {
29 let key = HeaderName::from_str(&key).ok()?;
30 let value = HeaderValue::from_str(&value).ok()?;
31 Some((key, value))
32 })
33 .collect()
34 }
35}
36
37impl<E: FromServerFnError> ClientRes<E> for BrowserResponse {
38 fn try_into_string(self) -> impl Future<Output = Result<String, E>> + Send {
39 SendWrapper::new(async move {
42 self.0.text().await.map_err(|e| {
43 ServerFnErrorErr::Deserialization(e.to_string())
44 .into_app_error()
45 })
46 })
47 }
48
49 fn try_into_bytes(self) -> impl Future<Output = Result<Bytes, E>> + Send {
50 SendWrapper::new(async move {
53 self.0.binary().await.map(Bytes::from).map_err(|e| {
54 ServerFnErrorErr::Deserialization(e.to_string())
55 .into_app_error()
56 })
57 })
58 }
59
60 fn try_into_stream(
61 self,
62 ) -> Result<impl Stream<Item = Result<Bytes, Bytes>> + Send + 'static, E>
63 {
64 let stream = ReadableStream::from_raw(self.0.body().unwrap())
65 .into_stream()
66 .map(|data| match data {
67 Err(e) => {
68 web_sys::console::error_1(&e);
69 Err(E::from_server_fn_error(ServerFnErrorErr::Request(
70 format!("{e:?}"),
71 ))
72 .ser())
73 }
74 Ok(data) => {
75 let data = data.unchecked_into::<Uint8Array>();
76 let mut buf = Vec::new();
77 let length = data.length();
78 buf.resize(length as usize, 0);
79 data.copy_to(&mut buf);
80 Ok(Bytes::from(buf))
81 }
82 });
83 Ok(SendWrapper::new(stream))
84 }
85
86 fn status(&self) -> u16 {
87 self.0.status()
88 }
89
90 fn status_text(&self) -> String {
91 self.0.status_text()
92 }
93
94 fn location(&self) -> String {
95 self.0
96 .headers()
97 .get("Location")
98 .unwrap_or_else(|| self.0.url())
99 }
100
101 fn has_redirect(&self) -> bool {
102 self.0.headers().get(REDIRECT_HEADER).is_some()
103 }
104}