1use crate::{Error, Result};
3use serde::Serialize;
4use std::borrow::Cow;
5
6#[derive(Debug, Clone)]
10pub struct Client {
11 inner: reqwest::Client,
12}
13pub enum Body {
15 FromString(String),
16 FromFile(tokio::fs::File),
17}
18impl From<Body> for reqwest::Body {
19 fn from(value: Body) -> Self {
20 match value {
21 Body::FromString(s) => reqwest::Body::from(s),
22 Body::FromFile(f) => reqwest::Body::from(f),
23 }
24 }
25}
26pub struct QueryResponse {
28 pub text: String,
29 pub status_code: u16,
30 pub headers: Vec<(String, String)>,
31}
32impl QueryResponse {
33 async fn try_from_reqwest_response(response: reqwest::Response) -> Result<Self> {
34 let status_code = response.status().as_u16();
35 let headers = response
36 .headers()
37 .iter()
38 .map(|(header, value)| -> Result<_> {
39 let header = header.to_string();
40 let value = value
41 .to_str()
42 .map_err(|_| Error::web(format!("Error parsing response header: {value:?}")))?
43 .to_owned();
44 Ok((header, value))
45 })
46 .collect::<Result<Vec<_>>>()?;
47 let text = response.text().await?;
48 Ok(QueryResponse {
49 text,
50 status_code,
51 headers,
52 })
53 }
54}
55
56impl Client {
57 pub fn new() -> Result<Self> {
59 let inner = reqwest::Client::builder().build()?;
60 Ok(Self { inner })
61 }
62 #[cfg(feature = "rustls-tls")]
63 #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
64 pub fn new_rustls_tls() -> Result<Self> {
66 let inner = reqwest::Client::builder().use_rustls_tls().build()?;
67 Ok(Self { inner })
68 }
69 #[cfg(feature = "native-tls")]
70 #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
71 pub fn new_native_tls() -> Result<Self> {
73 let inner = reqwest::Client::builder().use_native_tls().build()?;
74 Ok(Self { inner })
75 }
76 #[cfg(feature = "reqwest")]
77 #[cfg_attr(docsrs, doc(cfg(feature = "reqwest")))]
78 pub fn new_from_reqwest_client(client: reqwest::Client) -> Self {
80 Self { inner: client }
81 }
82 pub async fn post_query<'a, I>(
84 &self,
85 url: impl AsRef<str>,
86 headers: impl IntoIterator<IntoIter = I>,
87 body: Body,
88 params: &(impl Serialize + ?Sized),
89 ) -> Result<QueryResponse>
90 where
91 I: Iterator<Item = (&'a str, Cow<'a, str>)>,
92 {
93 let mut request_builder = self.inner.post(url.as_ref()).body(body).query(params);
94 for (header, value) in headers {
95 request_builder = request_builder.header(header, value.as_ref());
96 }
97 let response = request_builder.send().await?;
98 QueryResponse::try_from_reqwest_response(response).await
99 }
100 pub async fn post_json_query<'a, I>(
103 &self,
104 url: impl AsRef<str>,
105 headers: impl IntoIterator<IntoIter = I>,
106 body_json: &(impl Serialize + ?Sized),
107 params: &(impl Serialize + ?Sized),
108 ) -> Result<QueryResponse>
109 where
110 I: Iterator<Item = (&'a str, Cow<'a, str>)>,
111 {
112 let mut request_builder = self.inner.post(url.as_ref()).json(body_json).query(params);
113 for (header, value) in headers {
114 request_builder = request_builder.header(header, value.as_ref());
115 }
116 let response = request_builder.send().await?;
117 QueryResponse::try_from_reqwest_response(response).await
118 }
119 pub async fn get_query<'a, I>(
121 &self,
122 url: impl AsRef<str>,
123 headers: impl IntoIterator<IntoIter = I>,
124 params: &(impl Serialize + ?Sized),
125 ) -> Result<QueryResponse>
126 where
127 I: Iterator<Item = (&'a str, Cow<'a, str>)>,
128 {
129 let mut request_builder = self.inner.get(url.as_ref()).query(params);
130 for (header, value) in headers {
131 request_builder = request_builder.header(header, value.as_ref());
132 }
133 let response = request_builder.send().await?;
134 QueryResponse::try_from_reqwest_response(response).await
135 }
136}