From ba3a1c68f503a0ecf92af37ee8f0198720786bab Mon Sep 17 00:00:00 2001 From: Maksim Ryndin Date: Wed, 28 Jun 2023 17:57:12 +0000 Subject: [PATCH] bump to 0.5.4; add reqwest middleware --- CHANGELOG.md | 6 ++ Cargo.toml | 3 +- src/clients.rs | 2 + src/clients/reqwest_middleware.rs | 101 ++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/clients/reqwest_middleware.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 18520ac..c153fa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.5.4] - 2023-06-28 + +### Fixed + +- Add support for reqwest middleware client + ## [0.5.3] - 2022-03-15 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 9d3773b..72e8c87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustify" -version = "0.5.3" +version = "0.5.4" authors = ["Joshua Gilman "] description = "A Rust library for interacting with HTTP API endpoints." license = "MIT" @@ -28,6 +28,7 @@ async-trait = "0.1.52" bytes = "1.1.0" http = "0.2.6" reqwest = { version = "0.11.10", default-features = false, optional = true } +reqwest-middleware = { version = "0.2.2", optional = true } rustify_derive = { version = "0.5.2", path = "rustify_derive" } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" diff --git a/src/clients.rs b/src/clients.rs index 6fd2a25..c477c29 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -2,3 +2,5 @@ //! varying HTTP clients. #[cfg(feature = "reqwest")] pub mod reqwest; +#[cfg(all(feature = "reqwest", feature = "reqwest-middleware"))] +pub mod reqwest_middleware; diff --git a/src/clients/reqwest_middleware.rs b/src/clients/reqwest_middleware.rs new file mode 100644 index 0000000..79b97d0 --- /dev/null +++ b/src/clients/reqwest_middleware.rs @@ -0,0 +1,101 @@ +//! Contains an implementation of [Client][crate::client::Client] being backed +//! by the [reqwest](https://docs.rs/reqwest/) crate. + +use crate::{client::Client as RustifyClient, errors::ClientError}; +use async_trait::async_trait; +use http::{Request, Response}; +use std::convert::TryFrom; + +/// A client based on the +/// [reqwest_middleware::ClientWithMiddleware][1] which can be used for executing +/// [Endpoints][crate::endpoint::Endpoint]. A backing instance of a +/// [reqwest_middleware::ClientWithMiddleware][1] is used to increase performance and to save certain +/// characteristics across sessions. A base URL is required and is used to +/// qualify the full path of any [Endpoints][crate::endpoint::Endpoint] which +/// are executed by this client. +/// +/// # Example +/// ``` +/// use rustify::clients::reqwest_middleware::ClientWithMiddleware; +/// use rustify::Endpoint; +/// use rustify_derive::Endpoint; +/// use serde::Serialize; +/// +/// #[derive(Debug, Endpoint, Serialize)] +/// #[endpoint(path = "my/endpoint")] +/// struct MyEndpoint {} +/// +/// # tokio_test::block_on(async { +/// let client = ClientWithMiddleware::default("http://myapi.com"); +/// let endpoint = MyEndpoint {}; +/// let result = endpoint.exec(&client).await; +/// # }) +/// ``` +/// +/// [1]: https://docs.rs/reqwest-middleware/latest/reqwest_middleware/struct.ClientWithMiddleware.html +pub struct ClientWithMiddleware { + pub http: reqwest_middleware::ClientWithMiddleware, + pub base: String, +} + +impl ClientWithMiddleware { + /// Creates a new instance of [ClientWithMiddleware] using the provided parameters. + pub fn new(base: &str, http: reqwest_middleware::ClientWithMiddleware) -> Self { + Self { + base: base.to_string(), + http, + } + } + + /// Creates a new instance of [ClientWithMiddleware] with a default instance of + /// [reqwest_middleware::ClientWithMiddleware][1]. + /// + /// [1]: https://docs.rs/reqwest-middleware/latest/reqwest_middleware/struct.ClientWithMiddleware.html + pub fn default(base: &str) -> Self { + Self { + base: base.to_string(), + http: reqwest_middleware::ClientBuilder::new(reqwest::Client::default()).build(), + } + } +} + +#[async_trait] +impl RustifyClient for ClientWithMiddleware { + fn base(&self) -> &str { + self.base.as_str() + } + + #[instrument(skip(self, req), err)] + async fn send(&self, req: Request>) -> Result>, ClientError> { + let request = reqwest::Request::try_from(req) + .map_err(|e| ClientError::ReqwestBuildError { source: e })?; + + let url_err = request.url().to_string(); + let method_err = request.method().to_string(); + let response = self + .http + .execute(request) + .await + .map_err(|e| ClientError::RequestError { + source: e.into(), + url: url_err, + method: method_err, + })?; + + let status_code = response.status().as_u16(); + let mut http_resp = http::Response::builder().status(status_code); + for v in response.headers().into_iter() { + http_resp = http_resp.header(v.0, v.1); + } + + http_resp + .body( + response + .bytes() + .await + .map_err(|e| ClientError::ResponseError { source: e.into() })? + .to_vec(), + ) + .map_err(|e| ClientError::ResponseError { source: e.into() }) + } +}