use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
#[cfg(all(feature = "reqwest-multipart", feature = "reqwest-middleware-multipart"))]
use reqwest::multipart::Form;
use reqwest::{Body, Client as VanillaClient, IntoUrl, Method, Request, Response};
use serde::Serialize;
use std::convert::TryFrom;
use std::fmt::Display;
use thiserror::Error;
#[cfg(feature = "reqwest-middleware")]
pub use anyhow::Error as MiddlewareError;
#[cfg(feature = "reqwest-middleware")]
pub use reqwest_middleware::ClientWithMiddleware as MiddlewareClient;
#[derive(Clone, Debug)]
pub enum Client {
Vanilla(VanillaClient),
#[cfg(feature = "reqwest-middleware")]
Middleware(MiddlewareClient),
}
impl From<VanillaClient> for Client {
fn from(value: VanillaClient) -> Self {
Self::Vanilla(value)
}
}
#[cfg(feature = "reqwest-middleware")]
impl From<MiddlewareClient> for Client {
fn from(value: MiddlewareClient) -> Self {
Self::Middleware(value)
}
}
#[derive(Error, Debug)]
pub enum Error {
#[cfg(feature = "reqwest-middleware")]
#[error("Middleware error: {0}")]
Middleware(#[from] anyhow::Error),
#[error("Request error: {0}")]
Reqwest(#[from] reqwest::Error),
}
#[cfg(feature = "reqwest-middleware")]
impl From<reqwest_middleware::Error> for Error {
fn from(value: reqwest_middleware::Error) -> Self {
match value {
reqwest_middleware::Error::Middleware(x) => Self::Middleware(x),
reqwest_middleware::Error::Reqwest(x) => Self::Reqwest(x),
}
}
}
impl Client {
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::GET, url)
}
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::POST, url)
}
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::PUT, url)
}
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::PATCH, url)
}
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::DELETE, url)
}
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::HEAD, url)
}
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
match self {
Self::Vanilla(c) => RequestBuilder::Vanilla(c.request(method, url)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => RequestBuilder::Middleware(c.request(method, url)),
}
}
pub async fn execute(&self, req: Request) -> Result<Response, Error> {
match self {
Self::Vanilla(c) => c.execute(req).await.map_err(Into::into),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => {
let mut ext = http::Extensions::new();
c.execute_with_extensions(req, &mut ext)
.await
.map_err(Into::into)
}
}
}
#[cfg(feature = "reqwest-middleware")]
pub async fn execute_with_extensions(
&self,
req: Request,
ext: &mut http::Extensions
) -> Result<Response, Error> {
match self {
Self::Vanilla(c) => c.execute(req).await.map_err(Into::into),
Self::Middleware(c) => c
.execute_with_extensions(req, ext)
.await
.map_err(Into::into),
}
}
}
#[must_use = "RequestBuilder does nothing until you 'send' it"]
#[derive(Debug)]
pub enum RequestBuilder {
Vanilla(reqwest::RequestBuilder),
#[cfg(feature = "reqwest-middleware")]
Middleware(reqwest_middleware::RequestBuilder),
}
impl RequestBuilder {
pub fn header<K, V>(self, key: K, value: V) -> Self
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
match self {
Self::Vanilla(c) => Self::Vanilla(c.header(key, value)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.header(key, value)),
}
}
pub fn headers(self, headers: HeaderMap) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.headers(headers)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.headers(headers)),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn version(self, version: reqwest::Version) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.version(version)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.version(version)),
}
}
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
where
U: Display,
P: Display,
{
match self {
Self::Vanilla(c) => Self::Vanilla(c.basic_auth(username, password)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.basic_auth(username, password)),
}
}
pub fn bearer_auth<T>(self, token: T) -> Self
where
T: Display,
{
match self {
Self::Vanilla(c) => Self::Vanilla(c.bearer_auth(token)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.bearer_auth(token)),
}
}
pub fn body<T: Into<Body>>(self, body: T) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.body(body)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.body(body)),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn timeout(self, timeout: std::time::Duration) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.timeout(timeout)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.timeout(timeout)),
}
}
#[cfg(all(feature = "reqwest-multipart", feature = "reqwest-middleware-multipart"))]
pub fn multipart(self, multipart: Form) -> Self {
match self {
#[cfg(feature = "reqwest-multipart")]
Self::Vanilla(c) => Self::Vanilla(c.multipart(multipart)),
#[cfg(feature = "reqwest-middleware-multipart")]
Self::Middleware(c) => Self::Middleware(c.multipart(multipart)),
}
}
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.query(query)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.query(query)),
}
}
pub fn form<T: Serialize + ?Sized>(self, form: &T) -> Self {
match self {
Self::Vanilla(c) => Self::Vanilla(c.form(form)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Self::Middleware(c.form(form)),
}
}
#[cfg(all(feature = "reqwest-json", feature = "reqwest-middleware-json"))]
pub fn json<T: Serialize + ?Sized>(self, json: &T) -> Self {
match self {
#[cfg(feature = "reqwest-json")]
Self::Vanilla(c) => Self::Vanilla(c.json(json)),
#[cfg(feature = "reqwest-middleware-json")]
Self::Middleware(c) => Self::Middleware(c.json(json)),
}
}
pub fn build(self) -> reqwest::Result<Request> {
match self {
Self::Vanilla(c) => c.build(),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => c.build(),
}
}
#[cfg(feature = "reqwest-middleware")]
pub fn with_extension<T: Clone + Send + Sync + 'static>(self, extension: T) -> Self {
match self {
Self::Middleware(c) => Self::Middleware(c.with_extension(extension)),
c @ Self::Vanilla(_) => c,
}
}
#[cfg(feature = "reqwest-middleware")]
pub fn extensions(&mut self) -> &mut http::Extensions {
match self {
Self::Vanilla(_) => panic!("attempted to get extensions of vanilla client"),
Self::Middleware(c) => c.extensions(),
}
}
pub async fn send(self) -> Result<Response, Error> {
match self {
Self::Vanilla(c) => c.send().await.map_err(Into::into),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => c.send().await.map_err(Into::into),
}
}
pub fn try_clone(&self) -> Option<Self> {
match self {
Self::Vanilla(c) => Some(Self::Vanilla(c.try_clone()?)),
#[cfg(feature = "reqwest-middleware")]
Self::Middleware(c) => Some(Self::Middleware(c.try_clone()?)),
}
}
}