unistore_http/
response.rs1use super::error::HttpError;
9use serde::de::DeserializeOwned;
10
11#[derive(Debug)]
13pub struct Response {
14 status: u16,
16
17 headers: Vec<(String, String)>,
19
20 body: Vec<u8>,
22
23 url: String,
25}
26
27impl Response {
28 #[cfg_attr(not(test), allow(dead_code))]
32 pub(crate) fn new(status: u16, headers: Vec<(String, String)>, body: Vec<u8>, url: String) -> Self {
33 Self {
34 status,
35 headers,
36 body,
37 url,
38 }
39 }
40
41 pub fn status(&self) -> u16 {
43 self.status
44 }
45
46 pub fn is_success(&self) -> bool {
48 (200..300).contains(&self.status)
49 }
50
51 pub fn is_informational(&self) -> bool {
53 (100..200).contains(&self.status)
54 }
55
56 pub fn is_redirection(&self) -> bool {
58 (300..400).contains(&self.status)
59 }
60
61 pub fn is_client_error(&self) -> bool {
63 (400..500).contains(&self.status)
64 }
65
66 pub fn is_server_error(&self) -> bool {
68 (500..600).contains(&self.status)
69 }
70
71 pub fn url(&self) -> &str {
73 &self.url
74 }
75
76 pub fn headers(&self) -> &[(String, String)] {
78 &self.headers
79 }
80
81 pub fn header(&self, name: &str) -> Option<&str> {
83 let name_lower = name.to_lowercase();
84 self.headers
85 .iter()
86 .find(|(k, _)| k.to_lowercase() == name_lower)
87 .map(|(_, v)| v.as_str())
88 }
89
90 pub fn content_type(&self) -> Option<&str> {
92 self.header("content-type")
93 }
94
95 pub fn content_length(&self) -> Option<usize> {
97 self.header("content-length")
98 .and_then(|v| v.parse().ok())
99 }
100
101 pub fn bytes(&self) -> &[u8] {
103 &self.body
104 }
105
106 pub fn into_bytes(self) -> Vec<u8> {
108 self.body
109 }
110
111 pub fn text(&self) -> Result<String, HttpError> {
113 String::from_utf8(self.body.clone())
114 .map_err(|e| HttpError::ResponseBody(format!("UTF-8 解码失败: {}", e)))
115 }
116
117 pub fn into_text(self) -> Result<String, HttpError> {
119 String::from_utf8(self.body)
120 .map_err(|e| HttpError::ResponseBody(format!("UTF-8 解码失败: {}", e)))
121 }
122
123 pub fn json<T: DeserializeOwned>(&self) -> Result<T, HttpError> {
125 serde_json::from_slice(&self.body)
126 .map_err(|e| HttpError::JsonDeserialize(e.to_string()))
127 }
128
129 pub fn into_json<T: DeserializeOwned>(self) -> Result<T, HttpError> {
131 serde_json::from_slice(&self.body)
132 .map_err(|e| HttpError::JsonDeserialize(e.to_string()))
133 }
134
135 pub fn error_for_status(self) -> Result<Self, HttpError> {
137 if self.is_success() {
138 Ok(self)
139 } else if self.is_client_error() {
140 Err(HttpError::ClientError(
141 self.status,
142 self.text().unwrap_or_else(|_| "Unknown error".into()),
143 ))
144 } else if self.is_server_error() {
145 Err(HttpError::ServerError(self.status))
146 } else {
147 Ok(self)
148 }
149 }
150
151 pub(crate) async fn from_reqwest(resp: reqwest::Response) -> Result<Self, HttpError> {
153 let status = resp.status().as_u16();
154 let url = resp.url().to_string();
155
156 let headers: Vec<(String, String)> = resp
157 .headers()
158 .iter()
159 .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
160 .collect();
161
162 let body = resp.bytes().await?.to_vec();
163
164 Ok(Self {
165 status,
166 headers,
167 body,
168 url,
169 })
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn mock_response(status: u16, body: &str) -> Response {
178 Response::new(
179 status,
180 vec![
181 ("content-type".into(), "application/json".into()),
182 ("content-length".into(), body.len().to_string()),
183 ],
184 body.as_bytes().to_vec(),
185 "http://example.com".into(),
186 )
187 }
188
189 #[test]
190 fn test_status_checks() {
191 assert!(mock_response(200, "").is_success());
192 assert!(mock_response(201, "").is_success());
193 assert!(mock_response(204, "").is_success());
194 assert!(!mock_response(400, "").is_success());
195 assert!(!mock_response(500, "").is_success());
196
197 assert!(mock_response(100, "").is_informational());
198 assert!(mock_response(301, "").is_redirection());
199 assert!(mock_response(404, "").is_client_error());
200 assert!(mock_response(503, "").is_server_error());
201 }
202
203 #[test]
204 fn test_headers() {
205 let resp = mock_response(200, "test");
206 assert_eq!(resp.content_type(), Some("application/json"));
207 assert_eq!(resp.content_length(), Some(4));
208 assert_eq!(resp.header("Content-Type"), Some("application/json")); }
210
211 #[test]
212 fn test_body_parsing() {
213 let resp = mock_response(200, "hello");
214 assert_eq!(resp.text().unwrap(), "hello");
215 assert_eq!(resp.bytes(), b"hello");
216 }
217
218 #[test]
219 fn test_json_parsing() {
220 let resp = mock_response(200, r#"{"name":"test"}"#);
221 let data: serde_json::Value = resp.json().unwrap();
222 assert_eq!(data["name"], "test");
223 }
224
225 #[test]
226 fn test_error_for_status() {
227 let ok = mock_response(200, "ok");
228 assert!(ok.error_for_status().is_ok());
229
230 let client_err = mock_response(404, "Not Found");
231 assert!(client_err.error_for_status().is_err());
232
233 let server_err = mock_response(500, "Internal Server Error");
234 assert!(server_err.error_for_status().is_err());
235 }
236}