use crate::error::Error as AlvError;
use anyhow::Result;
use futures::Future;
use reqwest::{header::HeaderMap, Client, Error, IntoUrl, Method, Response};
use serde::{de::DeserializeOwned, Serialize};
pub(crate) const EMPTY_BODY: Option<String> = None;
pub(crate) fn req<T, U>(
client: &Client,
method: Method,
url: U,
headers: Option<HeaderMap>,
json: Option<T>,
) -> impl Future<Output = std::result::Result<Response, Error>>
where
T: Serialize + Send + Sync,
U: IntoUrl,
{
let mut rb = client.request(method, url);
if let Some(headers) = headers {
rb = rb.headers(headers);
}
if let Some(json) = json {
rb = rb.json(&json);
}
rb.send()
}
pub(crate) async fn as_json<T>(res: Result<Response, Error>) -> Result<T>
where
T: DeserializeOwned,
{
res.map(to_json)?.await
}
#[allow(clippy::unused_async)]
pub(crate) async fn as_empty(res: Result<Response, Error>) -> Result<()> {
res.map(to_empty)?
}
async fn to_json<T>(res: Response) -> Result<T>
where
T: DeserializeOwned,
{
res.error_for_status()
.map(|res| async move { handle_text(res).await })?
.await
}
async fn handle_text<T>(res: Response) -> Result<T>
where
T: DeserializeOwned,
{
match res.text().await {
Ok(text) => {
let invalid_body = |e: serde_json::Error| -> anyhow::Error { invalid_body(&e, &text) };
serde_json::from_str::<T>(&text).map_err(invalid_body)
}
Err(e) => Err(e.into()),
}
}
fn invalid_body(e: &serde_json::Error, text: &str) -> anyhow::Error {
AlvError::invalid_body(format!("{text}: {e}")).into()
}
fn to_empty(res: Response) -> Result<()> {
res.error_for_status().map(|_| ()).map_err(Error::into)
}