reqwest_response_ext/
blocking.rs

1use super::*;
2
3/// Holds raw response body, while remembering desired shape of the success (`T`)
4/// and failure (`E`) variants.
5///
6#[derive(Clone, Debug)]
7pub struct TypedResponse<T, E> {
8    body: bytes::Bytes,
9    result: Result<PhantomData<T>, PhantomData<E>>,
10}
11
12impl<T, E> TypedResponse<T, E>
13where
14    T: de::DeserializeOwned,
15    E: de::DeserializeOwned + From<json::Error>,
16{
17    /// Converts `reqwest::blocking::Response` into `TypedResponse<T, E>`
18    ///
19    pub fn try_from_response(response: reqwest::blocking::Response) -> reqwest::Result<Self> {
20        let result = match response.status().is_success() {
21            false => Err(PhantomData),
22            true => Ok(PhantomData),
23        };
24
25        // Bail early on server error
26        if response.status().is_server_error() {
27            response.error_for_status_ref()?;
28        }
29
30        let body = response.bytes()?;
31
32        Ok(Self { body, result })
33    }
34
35    /// Access the raw HTTP response as bytes
36    ///
37    pub fn bytes(&self) -> &bytes::Bytes {
38        &self.body
39    }
40
41    /// Access the raw HTTP response body as text
42    ///
43    pub fn text(&self) -> Cow<'_, str> {
44        String::from_utf8_lossy(&self.body)
45    }
46
47    /// Convert this response into `Result<serde_json::Value, serde_json::Value>`
48    /// where `Ok` and `Err` variants are based on the original HTTP Status
49    /// In case the body is not a valid JSON by itself it creates a JSON object
50    /// with deserialization error as a string content.
51    ///
52    pub fn into_json(self) -> Result<json::Value, json::Value> {
53        let json_err = |e: json::Error| json::json! { e.to_string() };
54        match self.result {
55            Ok(_) => Ok(json::from_slice(&self.body).map_err(json_err)?),
56            Err(_) => Err(json::from_slice(&self.body).map_err(json_err)?),
57        }
58    }
59
60    /// Convert this response into `Result<T, E>` where `Ok` and `Err` variants
61    /// are based on the original HTTP Status and type parameters. In case of
62    /// JSON deserialization error it will be converted into `E`.
63    pub fn into_result(self) -> Result<T, E> {
64        match self.result {
65            Ok(_) => Ok(json::from_slice(&self.body)?),
66            Err(_) => Err(json::from_slice(&self.body)?),
67        }
68    }
69}
70
71pub trait ResponseExt: Sized {
72    fn try_from_response<T, E>(self) -> reqwest::Result<TypedResponse<T, E>>
73    where
74        T: de::DeserializeOwned + Send,
75        E: de::DeserializeOwned + From<json::Error> + Send;
76}
77
78impl ResponseExt for reqwest::blocking::Response {
79    fn try_from_response<T, E>(self) -> reqwest::Result<TypedResponse<T, E>>
80    where
81        T: de::DeserializeOwned + Send,
82        E: de::DeserializeOwned + From<json::Error> + Send,
83    {
84        TypedResponse::try_from_response(self)
85    }
86}