use std::{future::Future, pin::Pin};
use reqwest::{
Client, Response,
header::{HeaderMap, HeaderName, HeaderValue},
};
use crate::errors::QobuzApiError;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait HttpClient: Send + Sync {
fn get(
&self,
url: &str,
params: &[(&str, &str)],
) -> BoxFuture<'_, Result<Response, QobuzApiError>>;
fn post_form(
&self,
url: &str,
params: &[(&str, &str)],
) -> BoxFuture<'_, Result<Response, QobuzApiError>>;
fn get_with_auth(
&self,
url: &str,
token: &str,
range: Option<&str>,
) -> BoxFuture<'_, Result<Response, QobuzApiError>>;
}
pub struct ReqwestClient {
inner: Client,
}
impl ReqwestClient {
pub fn new(app_id: &str) -> Result<Self, QobuzApiError> {
let mut default_headers = HeaderMap::new();
if let Ok(val) = HeaderValue::from_str(app_id) {
default_headers.insert(HeaderName::from_static("x-app-id"), val);
}
let inner = Client::builder()
.user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0",
)
.default_headers(default_headers)
.build()?;
Ok(Self { inner })
}
#[must_use]
pub fn into_boxed(self) -> Box<dyn HttpClient> {
Box::new(self)
}
}
impl HttpClient for ReqwestClient {
fn get(
&self,
url: &str,
params: &[(&str, &str)],
) -> BoxFuture<'_, Result<Response, QobuzApiError>> {
let fut = self.inner.get(url).query(params).send();
Box::pin(async move {
let resp = fut.await?;
Ok(resp)
})
}
fn post_form(
&self,
url: &str,
params: &[(&str, &str)],
) -> BoxFuture<'_, Result<Response, QobuzApiError>> {
let params: Vec<(String, String)> = params
.iter()
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
.collect();
let fut = self.inner.post(url).form(¶ms);
Box::pin(async move {
let resp = fut.send().await?;
Ok(resp)
})
}
fn get_with_auth(
&self,
url: &str,
token: &str,
range: Option<&str>,
) -> BoxFuture<'_, Result<Response, QobuzApiError>> {
let mut req = self.inner.get(url).header("X-User-Auth-Token", token);
if let Some(r) = range {
req = req.header("Range", r);
}
let fut = req.send();
Box::pin(async move {
let resp = fut.await?;
Ok(resp)
})
}
}