use crate::{Error, Result};
use serde::Serialize;
use std::borrow::Cow;
#[derive(Debug, Clone)]
pub struct Client {
inner: reqwest::Client,
}
pub enum Body {
FromString(String),
FromFile(tokio::fs::File),
}
impl From<Body> for reqwest::Body {
fn from(value: Body) -> Self {
match value {
Body::FromString(s) => reqwest::Body::from(s),
Body::FromFile(f) => reqwest::Body::from(f),
}
}
}
pub struct QueryResponse {
pub text: String,
pub status_code: u16,
pub headers: Vec<(String, String)>,
}
impl QueryResponse {
async fn try_from_reqwest_response(response: reqwest::Response) -> Result<Self> {
let status_code = response.status().as_u16();
let headers = response
.headers()
.iter()
.map(|(header, value)| -> Result<_> {
let header = header.to_string();
let value = value
.to_str()
.map_err(|_| Error::web(format!("Error parsing response header: {:?}", value)))?
.to_owned();
Ok((header, value))
})
.collect::<Result<Vec<_>>>()?;
let text = response.text().await?;
Ok(QueryResponse {
text,
status_code,
headers,
})
}
}
impl Client {
pub fn new() -> Result<Self> {
let inner = reqwest::Client::builder().build()?;
Ok(Self { inner })
}
#[cfg(feature = "rustls-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))]
pub fn new_rustls_tls() -> Result<Self> {
let inner = reqwest::Client::builder().use_rustls_tls().build()?;
Ok(Self { inner })
}
#[cfg(feature = "native-tls")]
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
pub fn new_native_tls() -> Result<Self> {
let inner = reqwest::Client::builder().use_native_tls().build()?;
Ok(Self { inner })
}
#[cfg(feature = "reqwest")]
#[cfg_attr(docsrs, doc(cfg(feature = "reqwest")))]
pub fn new_from_reqwest_client(client: reqwest::Client) -> Self {
Self { inner: client }
}
pub async fn post_query<'a, I>(
&self,
url: impl AsRef<str>,
headers: impl IntoIterator<IntoIter = I>,
body: Body,
params: &(impl Serialize + ?Sized),
) -> Result<QueryResponse>
where
I: Iterator<Item = (&'a str, Cow<'a, str>)>,
{
let mut request_builder = self.inner.post(url.as_ref()).body(body).query(params);
for (header, value) in headers {
request_builder = request_builder.header(header, value.as_ref());
}
let response = request_builder.send().await?;
QueryResponse::try_from_reqwest_response(response).await
}
pub async fn post_json_query<'a, I>(
&self,
url: impl AsRef<str>,
headers: impl IntoIterator<IntoIter = I>,
body_json: &(impl Serialize + ?Sized),
params: &(impl Serialize + ?Sized),
) -> Result<QueryResponse>
where
I: Iterator<Item = (&'a str, Cow<'a, str>)>,
{
let mut request_builder = self.inner.post(url.as_ref()).json(body_json).query(params);
for (header, value) in headers {
request_builder = request_builder.header(header, value.as_ref());
}
let response = request_builder.send().await?;
QueryResponse::try_from_reqwest_response(response).await
}
pub async fn get_query<'a, I>(
&self,
url: impl AsRef<str>,
headers: impl IntoIterator<IntoIter = I>,
params: &(impl Serialize + ?Sized),
) -> Result<QueryResponse>
where
I: Iterator<Item = (&'a str, Cow<'a, str>)>,
{
let mut request_builder = self.inner.get(url.as_ref()).query(params);
for (header, value) in headers {
request_builder = request_builder.header(header, value.as_ref());
}
let response = request_builder.send().await?;
QueryResponse::try_from_reqwest_response(response).await
}
}