use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
use std::pin::Pin;
pub type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, reqwest::Error>> + Send>>;
pub struct HttpResponse {
pub status: u16,
pub content_length: Option<u64>,
pub body: ByteStream,
}
#[async_trait]
pub trait HttpClient: Send + Sync {
async fn get_bytes(&self, url: &str) -> Result<Bytes, reqwest::Error>;
async fn get_stream(&self, url: &str) -> Result<HttpResponse, reqwest::Error>;
}
#[derive(Clone)]
pub struct ReqwestClient {
client: reqwest::Client,
}
impl ReqwestClient {
pub fn new() -> Self {
Self {
client: reqwest::Client::new(),
}
}
pub fn with_client(client: reqwest::Client) -> Self {
Self { client }
}
}
impl Default for ReqwestClient {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl HttpClient for ReqwestClient {
async fn get_bytes(&self, url: &str) -> Result<Bytes, reqwest::Error> {
self.client.get(url).send().await?.bytes().await
}
async fn get_stream(&self, url: &str) -> Result<HttpResponse, reqwest::Error> {
use futures::StreamExt;
let response = self.client.get(url).send().await?;
let status = response.status().as_u16();
let content_length = response.content_length();
let body: ByteStream = Box::pin(response.bytes_stream().map(|result| result));
Ok(HttpResponse {
status,
content_length,
body,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reqwest_client_can_be_created() {
let _client = ReqwestClient::new();
let _client_default = ReqwestClient::default();
}
#[test]
fn reqwest_client_can_be_cloned() {
let client = ReqwestClient::new();
let _cloned = client.clone();
}
}