reqwest_response_ext/
lib.rs1#![cfg_attr(feature = "pedantic", warn(clippy::pedantic))]
8#![warn(clippy::use_self)]
9#![warn(clippy::map_flatten)]
10#![warn(clippy::map_unwrap_or)]
11#![warn(clippy::flat_map_option)]
12#![warn(deprecated_in_future)]
13#![warn(future_incompatible)]
14#![warn(noop_method_call)]
15#![warn(unreachable_pub)]
16#![warn(missing_debug_implementations)]
17#![warn(rust_2018_compatibility)]
18#![warn(rust_2021_compatibility)]
19#![warn(rust_2018_idioms)]
20#![warn(unused)]
21#![deny(warnings)]
22
23use std::borrow::Cow;
24use std::marker::PhantomData;
25
26use serde::de;
27use serde_json as json;
28
29#[cfg(feature = "blocking")]
30pub mod blocking;
31
32#[derive(Clone, Debug)]
36pub struct TypedResponse<T, E> {
37 body: bytes::Bytes,
38 result: Result<PhantomData<T>, PhantomData<E>>,
39}
40
41impl<T, E> TypedResponse<T, E>
42where
43 T: de::DeserializeOwned,
44 E: de::DeserializeOwned + From<json::Error>,
45{
46 pub async fn try_from_response(response: reqwest::Response) -> reqwest::Result<Self> {
49 let result = match response.status().is_success() {
50 false => Err(PhantomData),
51 true => Ok(PhantomData),
52 };
53
54 if response.status().is_server_error() {
56 response.error_for_status_ref()?;
57 }
58
59 let body = response.bytes().await?;
60
61 Ok(Self { body, result })
62 }
63
64 pub fn bytes(&self) -> &bytes::Bytes {
67 &self.body
68 }
69
70 pub fn text(&self) -> Cow<'_, str> {
73 String::from_utf8_lossy(&self.body)
74 }
75
76 pub fn into_json(self) -> Result<json::Value, json::Value> {
82 let json_err = |e: json::Error| json::json! { e.to_string() };
83 match self.result {
84 Ok(_) => Ok(json::from_slice(&self.body).map_err(json_err)?),
85 Err(_) => Err(json::from_slice(&self.body).map_err(json_err)?),
86 }
87 }
88
89 pub fn into_result(self) -> Result<T, E> {
93 match self.result {
94 Ok(_) => Ok(json::from_slice(&self.body)?),
95 Err(_) => Err(json::from_slice(&self.body)?),
96 }
97 }
98}
99
100#[async_trait::async_trait]
101pub trait ResponseExt: Sized {
102 async fn try_from_response<T, E>(self) -> reqwest::Result<TypedResponse<T, E>>
103 where
104 T: de::DeserializeOwned + Send,
105 E: de::DeserializeOwned + From<json::Error> + Send;
106}
107
108#[async_trait::async_trait]
109impl ResponseExt for reqwest::Response {
110 async fn try_from_response<T, E>(self) -> reqwest::Result<TypedResponse<T, E>>
111 where
112 T: de::DeserializeOwned + Send,
113 E: de::DeserializeOwned + From<json::Error> + Send,
114 {
115 TypedResponse::try_from_response(self).await
116 }
117}