use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tokio::runtime::Handle;
pub trait HttpService: Debug + Send + Sync + 'static {
fn call(
&self,
request: reqwest::Request,
) -> Pin<Box<dyn Future<Output = crate::Result<reqwest::Response>> + Send + '_>>;
}
#[derive(Debug, Clone)]
pub struct ReqwestService(reqwest::Client);
impl ReqwestService {
pub fn new(client: reqwest::Client) -> Self {
Self(client)
}
}
impl HttpService for ReqwestService {
fn call(
&self,
request: reqwest::Request,
) -> Pin<Box<dyn Future<Output = crate::Result<reqwest::Response>> + Send + '_>> {
let client = self.0.clone();
Box::pin(async move { Ok(client.execute(request).await?) })
}
}
#[derive(Debug)]
pub struct SpawnService {
inner: Arc<dyn HttpService>,
handle: Handle,
}
impl SpawnService {
pub fn new(inner: Arc<dyn HttpService>, handle: Handle) -> Self {
Self { inner, handle }
}
}
impl HttpService for SpawnService {
fn call(
&self,
request: reqwest::Request,
) -> Pin<Box<dyn Future<Output = crate::Result<reqwest::Response>> + Send + '_>> {
let inner = Arc::clone(&self.inner);
let handle = self.handle.clone();
Box::pin(async move {
match handle.spawn(async move { inner.call(request).await }).await {
Ok(result) => result,
Err(join_err) if join_err.is_cancelled() => {
Err(crate::Error::Generic {
source: Box::new(join_err),
})
}
Err(join_err) => {
panic!("I/O runtime task panicked: {join_err}")
}
}
})
}
}
pub(crate) fn make_service(
client: reqwest::Client,
runtime: Option<&Handle>,
) -> Arc<dyn HttpService> {
let base: Arc<dyn HttpService> = Arc::new(ReqwestService::new(client));
match runtime {
Some(handle) => Arc::new(SpawnService::new(base, handle.clone())),
None => base,
}
}