qubit_http/http_response.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! Buffered HTTP response type and methods.
10
11use bytes::Bytes;
12use http::{HeaderMap, StatusCode};
13use serde::de::DeserializeOwned;
14use url::Url;
15
16use crate::{HttpError, HttpResult};
17
18/// Complete HTTP response after the body has been read into memory.
19#[derive(Debug, Clone)]
20pub struct HttpResponse {
21 /// Response status code.
22 pub status: StatusCode,
23 /// Response headers.
24 pub headers: HeaderMap,
25 /// Response body bytes.
26 pub body: Bytes,
27 /// Final resolved URL.
28 pub url: Url,
29}
30
31impl HttpResponse {
32 /// Assembles a response from its parts (as produced by [`crate::HttpClient::execute`]).
33 ///
34 /// # Parameters
35 /// - `status`: HTTP status line code.
36 /// - `headers`: Response headers.
37 /// - `body`: Full body bytes.
38 /// - `url`: Final URL after redirects.
39 ///
40 /// # Returns
41 /// New [`HttpResponse`].
42 pub fn new(status: StatusCode, headers: HeaderMap, body: Bytes, url: Url) -> Self {
43 Self {
44 status,
45 headers,
46 body,
47 url,
48 }
49 }
50
51 /// Returns whether [`HttpResponse::status`] is a success code ([`StatusCode::is_success`]).
52 ///
53 /// # Returns
54 /// `true` for 2xx responses.
55 pub fn is_success(&self) -> bool {
56 self.status.is_success()
57 }
58
59 /// Interprets [`HttpResponse::body`] as UTF-8 text.
60 ///
61 /// # Returns
62 /// `Ok(String)` or [`HttpError::decode`] with status/URL context on invalid UTF-8.
63 pub fn text(&self) -> HttpResult<String> {
64 String::from_utf8(self.body.to_vec()).map_err(|error| {
65 HttpError::decode(format!(
66 "Failed to decode response body as UTF-8: {}",
67 error
68 ))
69 .with_status(self.status)
70 .with_url(self.url.clone())
71 })
72 }
73
74 /// Deserializes [`HttpResponse::body`] as JSON into `T`.
75 ///
76 /// # Type parameters
77 /// - `T`: Type implementing [`serde::de::DeserializeOwned`].
78 ///
79 /// # Returns
80 /// `Ok(T)` or [`HttpError::decode`] with status/URL context.
81 pub fn json<T>(&self) -> HttpResult<T>
82 where
83 T: DeserializeOwned,
84 {
85 serde_json::from_slice(&self.body).map_err(|error| {
86 HttpError::decode(format!("Failed to decode response JSON: {}", error))
87 .with_status(self.status)
88 .with_url(self.url.clone())
89 })
90 }
91}