rustolio_utils/http/response/
mod.rs1#[cfg(not(target_arch = "wasm32"))]
12mod builder;
13
14use bytes::Bytes;
15
16use crate::bytes::IntoUtf8Bytes;
17#[cfg(not(target_arch = "wasm32"))]
18use crate::threadsafe;
19
20use super::{Error, HeaderName, HeaderValue, Incoming, Outgoing, Result, StatusCode, Version};
21
22#[cfg(not(target_arch = "wasm32"))]
23pub use builder::Builder;
24
25pub struct Response<B = Outgoing>(
26 #[cfg(target_arch = "wasm32")] web_sys::Response,
27 #[cfg(target_arch = "wasm32")] B,
28 #[cfg(not(target_arch = "wasm32"))] http::Response<B>,
29);
30
31impl Response<Incoming> {
32 #[cfg(not(target_arch = "wasm32"))]
33 pub fn from_inner(res: http::Response<Incoming>) -> Self {
34 Self(res)
35 }
36 #[cfg(target_arch = "wasm32")]
37 pub fn from_inner(res: web_sys::Response) -> Self {
38 Self(res, Incoming)
39 }
40}
41
42impl Response<Outgoing> {
43 #[cfg(not(target_arch = "wasm32"))]
44 pub fn into_inner(self) -> http::Response<Outgoing> {
45 self.0
46 }
47}
48
49impl Response<()> {
50 #[cfg(not(target_arch = "wasm32"))]
51 pub fn builder() -> Builder<()> {
52 Builder::new()
53 }
54}
55
56impl<B> Response<B> {
57 pub fn status(&self) -> StatusCode {
58 #[cfg(target_arch = "wasm32")]
59 {
60 StatusCode::try_from(self.0.status())
61 .expect("Response must contain a valid status code")
62 }
63 #[cfg(not(target_arch = "wasm32"))]
64 {
65 self.0.status()
66 }
67 }
68
69 #[cfg(not(target_arch = "wasm32"))]
70 pub fn header(&self, key: impl IntoUtf8Bytes) -> Option<&HeaderValue> {
71 let key = key.into();
72 let key: http::HeaderName = key.to_vec().try_into().ok()?;
73 self.0.headers().get(&key)
74 }
75
76 pub fn is_success(&self) -> bool {
77 self.status().is_success()
78 }
79
80 pub fn body(&self) -> &B {
81 #[cfg(target_arch = "wasm32")]
82 {
83 &self.1
84 }
85 #[cfg(not(target_arch = "wasm32"))]
86 {
87 self.0.body()
88 }
89 }
90
91 pub fn into_body(self) -> B {
92 #[cfg(target_arch = "wasm32")]
93 {
94 self.1
95 }
96 #[cfg(not(target_arch = "wasm32"))]
97 {
98 self.0.into_body()
99 }
100 }
101}
102
103#[cfg(target_arch = "wasm32")]
104impl Response<Incoming> {
105 pub async fn text(self) -> Result<Response<String>> {
106 let text = wasm_bindgen_futures::JsFuture::from(self.0.text().map_err(|_| Error::Recv)?)
107 .await
108 .map_err(|_| Error::Recv)?;
109 let text = text.try_into().map_err(|_| Error::Parse)?;
110 Ok(Response(self.0, text))
111 }
112
113 pub async fn json<T: serde::de::DeserializeOwned>(self) -> Result<Response<T>> {
114 let json = wasm_bindgen_futures::JsFuture::from(self.0.json().map_err(|_| Error::Recv)?)
115 .await
116 .map_err(|_| Error::Recv)?;
117 let json = serde_wasm_bindgen::from_value(json).map_err(|_| Error::Parse)?;
118 Ok(Response(self.0, json))
119 }
120
121 pub async fn encoded<T: crate::prelude::Decode>(self) -> Result<Response<T>> {
123 self.bytes().await.and_then(|b| {
124 let decoded: T =
125 crate::bytes::encoding::decode_from_bytes(b.1).map_err(|_| Error::Parse)?;
126 Ok(Response(b.0, decoded))
127 })
128 }
129
130 pub async fn bytes(self) -> Result<Response<Bytes>> {
131 let bytes =
132 wasm_bindgen_futures::JsFuture::from(self.0.array_buffer().map_err(|_| Error::Recv)?)
133 .await
134 .map_err(|_| Error::Recv)?;
135 let bytes = Bytes::from_owner(js_sys::Uint8Array::new(&bytes).to_vec());
136 Ok(Response(self.0, bytes))
137 }
138}
139
140#[cfg(not(target_arch = "wasm32"))]
141impl<B> Response<B>
142where
143 B: hyper::body::Body<Error: threadsafe::Error>,
144{
145 pub async fn text(self) -> Result<Response<String>> {
146 use http_body_util::BodyExt;
147
148 let (parts, body) = self.0.into_parts();
149
150 let body = body.collect().await.map_err(Error::body)?;
151 let Ok(text) = String::from_utf8(body.to_bytes().to_vec()) else {
152 return Err(Error::InvalidType);
153 };
154
155 Ok(Response(http::Response::from_parts(parts, text)))
156 }
157
158 pub async fn json<T: serde::de::DeserializeOwned>(self) -> Result<Response<T>> {
159 use http_body_util::BodyExt;
160
161 let Some(ty) = self.header(HeaderName::CONTENT_TYPE) else {
162 return Err(Error::InvalidType);
163 };
164 if !ty
165 .to_str()
166 .map_err(|_| Error::InvalidType)?
167 .starts_with("application/json")
168 {
169 return Err(Error::InvalidType);
170 }
171
172 let (parts, body) = self.0.into_parts();
173
174 let body = body.collect().await.map_err(Error::body)?;
175 let Ok(json) = serde_json::from_slice(&body.to_bytes()) else {
176 return Err(Error::InvalidType);
177 };
178
179 Ok(Response(http::Response::from_parts(parts, json)))
180 }
181
182 pub async fn encoded<T: crate::prelude::Decode>(self) -> Result<Response<T>> {
184 self.bytes().await.and_then(|b| {
185 let (parts, body) = b.0.into_parts();
186
187 let decoded: T =
188 crate::bytes::encoding::decode_from_bytes(body).map_err(Error::body)?;
189
190 Ok(Response(http::Response::from_parts(parts, decoded)))
191 })
192 }
193
194 pub async fn bytes(self) -> Result<Response<Bytes>> {
195 use http_body_util::BodyExt;
196
197 let (parts, body) = self.0.into_parts();
198
199 let body = body.collect().await.map_err(Error::body)?;
200 let bytes = body.to_bytes();
201
202 Ok(Response(http::Response::from_parts(parts, bytes)))
203 }
204}