#![forbid(unsafe_code, missing_docs)]
pub mod client;
#[cfg(feature = "sync")]
pub mod sync;
mod download_options;
mod error;
mod resources;
mod token;
use crate::resources::service_account::ServiceAccount;
pub use crate::{
client::Client,
error::*,
resources::{
bucket::{Bucket, NewBucket},
object::{ListRequest, Object},
*,
},
token::{Token, TokenCache},
};
pub use download_options::DownloadOptions;
use tokio::sync::Mutex;
lazy_static::lazy_static! {
static ref IAM_TOKEN_CACHE: Mutex<Token> = Mutex::new(Token::new(
"https://www.googleapis.com/auth/iam"
));
pub static ref SERVICE_ACCOUNT: ServiceAccount = ServiceAccount::get();
}
#[cfg(feature = "global-client")]
lazy_static::lazy_static! {
static ref CLOUD_CLIENT: client::Client = client::Client::default();
}
pub type Result<T> = std::result::Result<T, crate::Error>;
const BASE_URL: &str = "https://storage.googleapis.com/storage/v1";
fn from_str<'de, T, D>(deserializer: D) -> std::result::Result<T, D::Error>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
D: serde::Deserializer<'de>,
{
use serde::de::Deserialize;
let s = String::deserialize(deserializer)?;
T::from_str(&s).map_err(serde::de::Error::custom)
}
fn from_str_opt<'de, T, D>(deserializer: D) -> std::result::Result<Option<T>, D::Error>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
D: serde::Deserializer<'de>,
{
let s: std::result::Result<serde_json::Value, _> =
serde::Deserialize::deserialize(deserializer);
match s {
Ok(serde_json::Value::String(s)) => T::from_str(&s)
.map_err(serde::de::Error::custom)
.map(Option::from),
Ok(serde_json::Value::Number(num)) => T::from_str(&num.to_string())
.map_err(serde::de::Error::custom)
.map(Option::from),
Ok(_value) => Err(serde::de::Error::custom("Incorrect type")),
Err(_) => Ok(None),
}
}
#[cfg(all(test, feature = "global-client", feature = "sync"))]
fn read_test_bucket_sync() -> Bucket {
crate::runtime().unwrap().block_on(read_test_bucket())
}
#[cfg(all(test, feature = "global-client"))]
async fn read_test_bucket() -> Bucket {
dotenv::dotenv().ok();
let name = std::env::var("TEST_BUCKET").unwrap();
match Bucket::read(&name).await {
Ok(bucket) => bucket,
Err(_not_found) => Bucket::create(&NewBucket {
name,
..NewBucket::default()
})
.await
.unwrap(),
}
}
#[cfg(all(test, feature = "global-client", feature = "sync"))]
fn create_test_bucket_sync(name: &str) -> Bucket {
crate::runtime().unwrap().block_on(create_test_bucket(name))
}
#[cfg(all(test, feature = "global-client"))]
async fn create_test_bucket(name: &str) -> Bucket {
std::thread::sleep(std::time::Duration::from_millis(1500));
dotenv::dotenv().ok();
let base_name = std::env::var("TEST_BUCKET").unwrap();
let name = format!("{}-{}", base_name, name);
let new_bucket = NewBucket {
name,
..NewBucket::default()
};
match Bucket::create(&new_bucket).await {
Ok(bucket) => bucket,
Err(_alread_exists) => Bucket::read(&new_bucket.name).await.unwrap(),
}
}
#[cfg(feature = "sync")]
fn runtime() -> Result<tokio::runtime::Runtime> {
Ok(tokio::runtime::Builder::new_current_thread()
.thread_name("cloud-storage-worker")
.enable_time()
.enable_io()
.build()?)
}