Skip to content

Commit 22d640f

Browse files
committed
refactor: add Authorize abstraction
1 parent 6ca7f16 commit 22d640f

File tree

7 files changed

+163
-273
lines changed

7 files changed

+163
-273
lines changed

src/authorize.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use core::fmt;
2+
use std::sync::Arc;
3+
4+
use reqwest::Request;
5+
6+
use crate::credentials::{AuthorizationError, Credentials};
7+
8+
// AUTHORIZE ///////////////////////////////////////////////////////////////////
9+
10+
pub trait Authorize: fmt::Debug + Send + Sync + 'static {
11+
type Error: Send + 'static;
12+
13+
/// Appends an `Authorization` header to the `request`, if this authorizer
14+
/// has credentials and unless it is already set.
15+
///
16+
/// Returns `true` if the `Authorization` header was inserted.
17+
///
18+
/// # Errors
19+
///
20+
/// Upon failure to produce the `Authorization` header value or if the request
21+
/// has too many headers.
22+
fn authorize(&self, request: &mut Request) -> Result<bool, Self::Error>;
23+
}
24+
25+
impl Authorize for Credentials {
26+
type Error = AuthorizationError;
27+
28+
#[inline]
29+
fn authorize(&self, request: &mut Request) -> Result<bool, Self::Error> {
30+
self.as_ref().authorize(request)
31+
}
32+
}
33+
34+
impl<T: Authorize> Authorize for Option<T> {
35+
type Error = T::Error;
36+
37+
#[inline]
38+
fn authorize(&self, request: &mut Request) -> Result<bool, Self::Error> {
39+
match self.as_ref() {
40+
None => Ok(false),
41+
Some(authorizer) => authorizer.authorize(request),
42+
}
43+
}
44+
}
45+
46+
impl<T: Authorize> Authorize for Arc<T> {
47+
type Error = T::Error;
48+
49+
#[inline]
50+
fn authorize(&self, request: &mut Request) -> Result<bool, Self::Error> {
51+
(&**self).authorize(request)
52+
}
53+
}

src/client.rs

Lines changed: 32 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,83 @@
1-
//! HTTP client
2-
31
use core::fmt;
42

53
use reqwest::{Request, Response};
64

7-
use crate::{
8-
credentials::{AuthorizationError, Credentials},
9-
execute::ExecuteRequest,
10-
};
5+
use crate::{authorize::Authorize, credentials::Credentials, execute::ExecuteRequest};
116

127
// CLIENT ERROR ////////////////////////////////////////////////////////////////
138

149
#[derive(Debug, thiserror::Error)]
15-
pub enum ClientError<E = reqwest::Error> {
10+
pub enum ClientError<E, A> {
1611
#[error("failed to authorize request, {0}")]
17-
Authorize(#[from] AuthorizationError),
12+
Authorize(A),
1813
#[error("failed to execute request, {0}")]
1914
Execute(E),
2015
}
2116

2217
// CLIENT //////////////////////////////////////////////////////////////////////
2318

24-
/// HTTP client with optional [`Credentials`].
25-
///
26-
/// When credentials are provided, the client, will ensure requests are authorized
27-
/// before they are executed.
28-
#[derive(Debug, Default, Clone)]
19+
/// HTTP client that authorize requests before execution.
20+
#[derive(Debug, Clone)]
2921
#[must_use]
30-
pub struct Client<T = reqwest::Client> {
31-
inner: T,
32-
credentials: Option<Credentials>,
22+
pub struct Client<T = reqwest::Client, A = Option<Credentials>> {
23+
executer: T,
24+
authorizer: A,
3325
}
3426

35-
impl<T> From<T> for Client<T> {
27+
impl<T, A: Default> From<T> for Client<T, A> {
3628
fn from(value: T) -> Self {
3729
Self {
38-
inner: value,
39-
credentials: None,
30+
executer: value,
31+
authorizer: A::default(),
4032
}
4133
}
4234
}
4335

44-
impl From<&Credentials> for Client {
45-
fn from(value: &Credentials) -> Self {
46-
Self::from(value.clone())
36+
impl Default for Client {
37+
fn default() -> Self {
38+
Self::from(reqwest::Client::default())
4739
}
4840
}
4941

50-
impl<T: Into<Box<str>>> From<Credentials<T>> for Client {
51-
fn from(value: Credentials<T>) -> Self {
42+
impl<T: fmt::Debug, A: fmt::Debug> Client<T, A> {
43+
#[cfg_attr(feature = "tracing", tracing::instrument)]
44+
pub fn new(executer: T, authorizer: A) -> Self {
5245
Self {
53-
inner: reqwest::Client::new(),
54-
credentials: Some(value.into()),
46+
executer,
47+
authorizer,
5548
}
5649
}
57-
}
5850

59-
impl Client {
60-
pub fn new() -> Self {
61-
Self::default()
51+
pub fn executer(&self) -> &T {
52+
&self.executer
6253
}
63-
}
6454

65-
impl<T: fmt::Debug> Client<T> {
66-
/// Sets the `credentials` to be used by the client to authorize HTTP request,
67-
/// discarding the current value, if any.
68-
#[cfg_attr(feature = "tracing", tracing::instrument)]
69-
pub fn set_credentials(&mut self, credentials: Option<Credentials>) {
70-
self.credentials = credentials;
55+
pub fn executer_mut(&mut self) -> &mut T {
56+
&mut self.executer
7157
}
7258

73-
#[cfg_attr(feature = "tracing", tracing::instrument)]
74-
fn set_credentials_from<U: Into<Box<str>> + fmt::Debug>(
75-
&mut self,
76-
credentials: Option<Credentials<U>>,
77-
) {
78-
self.credentials = credentials.map(Credentials::into);
59+
pub fn authorizer(&self) -> &A {
60+
&self.authorizer
7961
}
8062

81-
/// Fills the `credentials` to be used by the client to authorize HTTP request,
82-
/// discarding the current value, if any.
83-
pub fn with_credentials<U: Into<Box<str>> + fmt::Debug>(
84-
mut self,
85-
credentials: impl Into<Option<Credentials<U>>>,
86-
) -> Self {
87-
self.set_credentials_from(credentials.into());
88-
self
89-
}
90-
91-
/// Returns the credentials that will be used by this client to authorized
92-
/// subsequent HTTP requests.
93-
#[cfg_attr(feature = "tracing", tracing::instrument)]
94-
pub fn credentials(&self) -> Option<Credentials<&str>> {
95-
self.credentials.as_ref().map(Credentials::as_ref)
96-
}
97-
98-
/// Returns a shared reference to the inner HTTP client.
99-
#[cfg_attr(feature = "tracing", tracing::instrument)]
100-
pub fn inner(&self) -> &T {
101-
&self.inner
102-
}
103-
104-
/// Appends an `Authorization` header to the `request`, if this client has credentials and unless it is already set.
105-
///
106-
/// Returns `true` if the `Authorization` header was inserted.
107-
///
108-
/// # Errors
109-
///
110-
/// Upon failure to produce the header value.
111-
///
112-
/// If the client doesn't have credentials, this method is infallible.
113-
#[cfg_attr(feature = "tracing", tracing::instrument)]
114-
pub fn authorize(&self, request: &mut Request) -> Result<bool, AuthorizationError> {
115-
match self.credentials() {
116-
None => Ok(false),
117-
Some(credentials) => credentials.authorize(request),
118-
}
63+
pub fn authorizer_mut(&mut self) -> &mut A {
64+
&mut self.authorizer
11965
}
12066
}
12167

122-
impl<T: ExecuteRequest> ExecuteRequest for Client<T> {
123-
type Error = ClientError<T::Error>;
68+
impl<T: ExecuteRequest, A: Authorize> ExecuteRequest for Client<T, A> {
69+
type Error = ClientError<T::Error, A::Error>;
12470

12571
fn execute_request(
12672
&self,
12773
mut request: Request,
12874
) -> impl Future<Output = Result<Response, Self::Error>> + Send + 'static {
12975
let result = self
76+
.authorizer
13077
.authorize(&mut request)
131-
.map(|_| self.inner.execute_request(request));
78+
.map_err(ClientError::Authorize)
79+
.map(|_| self.executer.execute_request(request));
13280

13381
async move { result?.await.map_err(ClientError::Execute) }
13482
}
13583
}
136-
137-
#[cfg(feature = "zeroize")]
138-
impl<T> Drop for Client<T> {
139-
fn drop(&mut self) {
140-
use zeroize::Zeroize;
141-
142-
if let Some(mut credentials) = self.credentials.take() {
143-
credentials.zeroize();
144-
}
145-
}
146-
}

0 commit comments

Comments
 (0)