pub(crate) mod bucket;
use std::sync::Arc;
use futures::Future;
use reqwest::{Method, RequestBuilder, Response, StatusCode, Url};
use crate::{errors::Error, token_provider::TokenProvider};
pub use bucket::{BucketClient, GcsBucketClient};
const GCS_API_URL: &str = "https://www.googleapis.com/storage/v1/";
#[derive(Clone)]
pub struct Client {
token_provider: Arc<dyn TokenProvider>,
client: reqwest::Client,
api_url: Arc<Url>,
}
impl Client {
pub fn new(token_provider: impl TokenProvider + 'static) -> Self {
Self::new_with_client(token_provider, Default::default())
}
pub fn new_with_client(
token_provider: impl TokenProvider + 'static,
client: reqwest::Client,
) -> Self {
Self {
token_provider: Arc::new(token_provider),
client,
api_url: Arc::new(Url::parse(GCS_API_URL).unwrap()),
}
}
pub fn into_bucket_client(self, bucket_name: String) -> bucket::GcsBucketClient {
bucket::GcsBucketClient::new(self, bucket_name)
}
async fn make_request<B, F>(
&self,
method: &Method,
path: &str,
make_request: B,
) -> Result<Response, Error>
where
B: FnOnce(RequestBuilder) -> F,
F: Future<Output = Result<Response, reqwest::Error>>,
{
self.make_request_to_url(
method,
&self.api_url.join(path).expect("malformed url"),
make_request,
)
.await
}
async fn make_request_to_url<B, F>(
&self,
method: &Method,
url: &Url,
make_request: B,
) -> Result<Response, Error>
where
B: FnOnce(RequestBuilder) -> F,
F: Future<Output = Result<Response, reqwest::Error>>,
{
let auth_token = self
.token_provider
.get_token()
.await
.map_err(Error::TokenFetch)?;
let builder = self
.client
.request(method.clone(), url.clone())
.bearer_auth(&auth_token.token);
let res = make_request(builder).await?;
if res.status() == StatusCode::UNAUTHORIZED {
self.token_provider.invalidate_token().await;
}
Ok(res)
}
}