use anyhow::Result;
use google_auth::TokenManager;
use serde::{Deserialize, Serialize};
#[derive(Clone)]
pub struct Client {
token_manager: TokenManager,
}
pub struct Bucket {
client: Client,
name: String,
}
impl Client {
pub async fn new() -> Result<Client> {
let tm = TokenManager::new(&[]).await?;
Ok(Client { token_manager: tm })
}
pub fn bucket(&self, name: &str) -> Bucket {
Bucket {
name: name.to_string(),
client: self.clone(),
}
}
}
impl Bucket {
pub async fn list(&mut self, prefix: &str) -> Result<Response> {
let url = format!(
"https://storage.googleapis.com/storage/v1/b/{}/o?prefix={}",
self.name, prefix
);
let access_token = self.client.token_manager.token().await?;
let client = reqwest::Client::new();
let res = client
.get(&url)
.header(reqwest::header::AUTHORIZATION, access_token)
.header(reqwest::header::ACCEPT, "application/json")
.send()
.await?;
let res: Response = res.json().await?;
Ok(res)
}
pub async fn insert(&mut self, data: Vec<u8>, file_name: &str) -> Result<Object> {
let url = format!(
"https://storage.googleapis.com/upload/storage/v1/b/{}/o?uploadType=media&name={}",
self.name, file_name
);
let access_token = self.client.token_manager.token().await?;
let client = reqwest::Client::new();
let len = data.len();
let res = client
.post(&url)
.body(data)
.header(reqwest::header::AUTHORIZATION, access_token)
.header(reqwest::header::CONTENT_LENGTH, len)
.header(
reqwest::header::CONTENT_TYPE,
"application/json;charset=UTF-8",
)
.send()
.await?;
let object: Object = res.json().await?;
Ok(object)
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Error {
error: ErrorPayload,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct ErrorPayload {
code: u32,
message: String,
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)]
pub enum Response {
Error(Error),
Objects(Objects),
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Objects {
kind: String,
pub items: Vec<Object>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Object {
kind: String,
id: String,
self_link: String,
media_link: String,
name: String,
bucket: String,
generation: String,
metageneration: String,
content_type: Option<String>,
storage_class: String,
size: String,
md5_hash: String,
crc32c: String,
etag: String,
time_created: String,
updated: String,
time_storage_class_updated: String,
}
impl Object {
pub async fn download(&self, mut client: Client) -> Result<String> {
let http_client = reqwest::Client::new();
let resp = http_client
.get(&self.media_link)
.header(
reqwest::header::AUTHORIZATION,
client.token_manager.token().await?,
)
.header(
reqwest::header::AUTHORIZATION,
client.token_manager.token().await?,
)
.send()
.await?;
let body = resp.text().await?;
Ok(body)
}
}