use std::env;
use std::fs::File;
use std::sync::Arc;
use json::json;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use tokio::sync::Mutex;
use crate::authorize::{ApplicationCredentials, TokenManager};
use crate::storage::api::bucket::{BucketResource, BucketResources};
use crate::storage::{Bucket, Error};
#[derive(Clone)]
pub struct Client {
pub(crate) project_name: String,
pub(crate) client: Arc<reqwest::Client>,
pub(crate) token_manager: Arc<Mutex<TokenManager>>,
}
impl Client {
#[allow(unused)]
pub(crate) const DOMAIN_NAME: &'static str = "storage.googleapis.com";
pub(crate) const ENDPOINT: &'static str = "https://storage.googleapis.com/storage/v1";
pub(crate) const UPLOAD_ENDPOINT: &'static str =
"https://storage.googleapis.com/upload/storage/v1";
pub(crate) const SCOPES: [&'static str; 2] = [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/devstorage.full_control",
];
#[allow(dead_code)]
pub(crate) fn uri(uri: &str) -> String {
if uri.starts_with('/') {
format!("{}{}", Client::ENDPOINT, uri)
} else {
format!("{}/{}", Client::ENDPOINT, uri)
}
}
pub async fn new(project_name: impl Into<String>) -> Result<Client, Error> {
let path = env::var("GOOGLE_APPLICATION_CREDENTIALS")?;
let file = File::open(path)?;
let creds = json::from_reader(file)?;
Client::from_credentials(project_name, creds).await
}
pub async fn from_credentials(
project_name: impl Into<String>,
creds: ApplicationCredentials,
) -> Result<Client, Error> {
let client = reqwest::Client::builder()
.build()?;
Ok(Client {
client: Arc::new(client),
project_name: project_name.into(),
token_manager: Arc::new(Mutex::new(TokenManager::new(
creds,
Client::SCOPES.as_ref(),
))),
})
}
pub async fn bucket(&mut self, name: &str) -> Result<Bucket, Error> {
let inner = &self.client;
let uri = format!(
"{}/b/{}",
Client::ENDPOINT,
utf8_percent_encode(name, NON_ALPHANUMERIC),
);
let token = self.token_manager.lock().await.token().await?;
let request = inner
.get(uri.as_str())
.header("authorization", token)
.send();
let response = request.await?;
let bucket = response
.error_for_status()?
.json::<BucketResource>()
.await?;
Ok(Bucket::new(self.clone(), bucket.name))
}
pub async fn buckets(&mut self) -> Result<Vec<Bucket>, Error> {
let inner = &self.client;
let uri = format!("{}/b", Client::ENDPOINT);
let token = self.token_manager.lock().await.token().await?;
let request = inner
.get(uri.as_str())
.query(&[("project", self.project_name.as_str())])
.header("authorization", token)
.send();
let response = request.await?;
let resources = response
.error_for_status()?
.json::<BucketResources>()
.await?;
let buckets = resources
.items
.into_iter()
.map(|resource| Bucket::new(self.clone(), resource.name))
.collect();
Ok(buckets)
}
pub async fn create_bucket(&mut self, name: &str) -> Result<Bucket, Error> {
let inner = &self.client;
let uri = format!("{}/b", Client::ENDPOINT);
let body = json!({
"kind": "storage#bucket",
"name": name,
});
let token = self.token_manager.lock().await.token().await?;
let request = inner
.post(uri.as_str())
.query(&[("project", self.project_name.as_str())])
.header("authorization", token)
.json(&body)
.send();
let response = request.await?;
let bucket = response
.error_for_status()?
.json::<BucketResource>()
.await?;
Ok(Bucket::new(self.clone(), bucket.name))
}
}