Skip to main content

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}