use std::future::Future;
use std::sync::Arc;
use async_trait::async_trait;
use http::Extensions;
use reqwest::{Method, Request, Response};
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("middleware failed: {0}")]
Middleware(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
}
#[async_trait]
pub trait Middleware: 'static + Send + Sync {
async fn handle(
&self,
req: Request,
extensions: &mut Extensions,
next: Next<'_>,
) -> Result<Response>;
}
#[derive(Clone)]
pub struct Next<'a> {
client: &'a ClientWithMiddleware,
remaining: &'a [Arc<dyn Middleware>],
}
impl Next<'_> {
pub(crate) fn new<'a>(
client: &'a ClientWithMiddleware,
remaining: &'a [Arc<dyn Middleware>],
) -> Next<'a> {
Next { client, remaining }
}
pub fn run(
self,
req: Request,
extensions: &mut Extensions,
) -> impl Future<Output = Result<Response>> {
let client = self.client;
let remaining = self.remaining;
async move { execute_with_chain(client, remaining, req, extensions).await }
}
}
pub(crate) async fn execute_with_chain(
client: &ClientWithMiddleware,
middleware: &[Arc<dyn Middleware>],
req: Request,
extensions: &mut Extensions,
) -> Result<Response> {
if let Some((current, rest)) = middleware.split_first() {
current
.handle(req, extensions, Next::new(client, rest))
.await
} else {
client.client.execute(req).await.map_err(Error::Reqwest)
}
}
#[derive(Clone)]
pub struct ClientWithMiddleware {
client: reqwest::Client,
middleware: Arc<[Arc<dyn Middleware>]>,
}
impl ClientWithMiddleware {
pub(crate) fn new(client: reqwest::Client, middleware: Arc<[Arc<dyn Middleware>]>) -> Self {
Self { client, middleware }
}
pub fn middleware_count(&self) -> usize {
self.middleware.len()
}
pub fn with_middleware(mut self, middleware: Arc<dyn Middleware>) -> Self {
let mut mws = Vec::with_capacity(self.middleware.len() + 1);
mws.extend(self.middleware.iter().cloned());
mws.push(middleware);
self.middleware = mws.into();
self
}
pub async fn execute(&self, req: Request) -> Result<Response> {
let mut extensions = Extensions::new();
self.execute_with_extensions(req, &mut extensions).await
}
pub async fn execute_with_extensions(
&self,
req: Request,
extensions: &mut Extensions,
) -> Result<Response> {
execute_with_chain(self, &self.middleware, req, extensions).await
}
pub fn get(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::GET, url)
}
pub fn post(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::POST, url)
}
pub fn put(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::PUT, url)
}
pub fn patch(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::PATCH, url)
}
pub fn head(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::HEAD, url)
}
pub fn delete(&self, url: impl Into<String>) -> RequestBuilder {
self.request(Method::DELETE, url)
}
pub fn request(&self, method: Method, url: impl Into<String>) -> RequestBuilder {
RequestBuilder {
client: self.clone(),
inner: self.client.request(method, url.into()),
extensions: Vec::new(),
}
}
}
impl std::fmt::Debug for ClientWithMiddleware {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClientWithMiddleware")
.field("client", &self.client)
.field("middleware", &self.middleware.len())
.finish()
}
}
#[derive(Clone)]
pub struct ClientBuilder {
client: reqwest::Client,
middleware: Vec<Arc<dyn Middleware>>,
}
impl ClientBuilder {
pub fn new(client: reqwest::Client) -> Self {
Self {
client,
middleware: Vec::new(),
}
}
pub fn with<M: Middleware>(self, middleware: M) -> Self {
self.with_middleware(Arc::new(middleware))
}
pub fn with_middleware(mut self, middleware: Arc<dyn Middleware>) -> Self {
self.middleware.push(middleware);
self
}
pub fn build(self) -> ClientWithMiddleware {
ClientWithMiddleware::new(self.client, self.middleware.into())
}
}
type ExtensionApplier = Arc<dyn Fn(&mut Extensions) + Send + Sync>;
pub struct RequestBuilder {
client: ClientWithMiddleware,
inner: reqwest::RequestBuilder,
extensions: Vec<ExtensionApplier>,
}
impl RequestBuilder {
pub fn with_extension<T: Clone + Send + Sync + 'static>(mut self, value: T) -> Self {
self.extensions.push(Arc::new(move |ext: &mut Extensions| {
ext.insert(value.clone());
}));
self
}
pub fn header<K, V>(mut self, key: K, value: V) -> Self
where
http::header::HeaderName: TryFrom<K>,
<http::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
http::HeaderValue: TryFrom<V>,
<http::HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
self.inner = self.inner.header(key, value);
self
}
pub fn headers(mut self, headers: reqwest::header::HeaderMap) -> Self {
self.inner = self.inner.headers(headers);
self
}
pub fn query<T: serde::Serialize + ?Sized>(mut self, params: &T) -> Self {
self.inner = self.inner.query(params);
self
}
pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
self.inner = self.inner.timeout(timeout);
self
}
#[cfg(feature = "json")]
pub fn json<T: serde::Serialize + ?Sized>(mut self, body: &T) -> Self {
self.inner = self.inner.json(body);
self
}
pub fn bearer_auth(mut self, token: impl std::fmt::Display) -> Self {
self.inner = self.inner.bearer_auth(token);
self
}
pub fn basic_auth<U, P>(mut self, username: U, password: Option<P>) -> Self
where
U: std::fmt::Display,
P: std::fmt::Display,
{
self.inner = self.inner.basic_auth(username, password);
self
}
pub fn body(mut self, body: impl Into<reqwest::Body>) -> Self {
self.inner = self.inner.body(body);
self
}
pub fn form<T: serde::Serialize + ?Sized>(mut self, form: &T) -> Self {
self.inner = self.inner.form(form);
self
}
#[cfg(feature = "multipart")]
pub fn multipart(mut self, form: reqwest::multipart::Form) -> Self {
self.inner = self.inner.multipart(form);
self
}
pub fn build(self) -> Result<Request> {
Ok(self.inner.build()?)
}
pub fn try_clone(&self) -> Option<Self> {
Some(Self {
client: self.client.clone(),
inner: self.inner.try_clone()?,
extensions: self.extensions.clone(),
})
}
pub async fn send(self) -> Result<Response> {
let Self {
client,
inner,
extensions: appliers,
} = self;
let request = inner.build()?;
let mut extensions = Extensions::new();
for apply in &appliers {
apply(&mut extensions);
}
client
.execute_with_extensions(request, &mut extensions)
.await
}
}