use std::{path::PathBuf, str::FromStr};
use aws_sdk_s3::types::StorageClass as S3StorageClass;
use once_cell::sync::OnceCell;
use serde::{de, Deserialize, Deserializer};
pub const UPLOAD_PART_SIZE: u64 = 8 * 1024 * 1024;
pub const UPLOAD_READ_SIZE: u64 = 1024 * 1024;
pub const CONCURRENT_UPLOAD_TASKS: u16 = 8;
pub const CONCURRENT_DOWNLOADER_TASKS: u16 = 8;
pub const CONCURRENT_SYNC_TASKS: u16 = 4;
pub const REQUEST_RETRIES: u16 = 5;
pub const CONCURRENT_WRITER_TASKS: u16 = 1;
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default, deserialize_with = "deserialize_storage_class")]
storage_class: StorageClass,
#[serde(default)]
upload_part_size: UploadPartSize,
#[serde(default)]
upload_read_size: UploadReadSize,
#[serde(default)]
concurrent_upload_tasks: ConcurrentUploadTasks,
#[serde(default)]
concurrent_downloader_tasks: ConcurrentDownloaderTasks,
#[serde(default)]
concurrent_sync_tasks: ConcurrentSyncTasks,
#[serde(default)]
request_retries: RequestRetries,
#[serde(default)]
concurrent_writer_tasks: ConcurrentWriterTasks,
#[serde(default)]
temp_dir_path: TempDirPath,
}
#[derive(Debug, Deserialize, Default)]
#[serde(transparent)]
struct TempDirPath(Option<PathBuf>);
#[derive(Debug)]
struct StorageClass(S3StorageClass);
impl Default for StorageClass {
fn default() -> Self {
StorageClass(S3StorageClass::Standard)
}
}
fn deserialize_storage_class<'de, D>(deserializer: D) -> Result<StorageClass, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
S3StorageClass::from_str(&s.to_uppercase())
.map(StorageClass)
.map_err(de::Error::custom)
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct UploadPartSize(u64);
impl Default for UploadPartSize {
fn default() -> Self {
UploadPartSize(UPLOAD_PART_SIZE)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct UploadReadSize(u64);
impl Default for UploadReadSize {
fn default() -> Self {
UploadReadSize(UPLOAD_READ_SIZE)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct ConcurrentUploadTasks(u16);
impl Default for ConcurrentUploadTasks {
fn default() -> Self {
ConcurrentUploadTasks(CONCURRENT_UPLOAD_TASKS)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct ConcurrentDownloaderTasks(u16);
impl Default for ConcurrentDownloaderTasks {
fn default() -> Self {
ConcurrentDownloaderTasks(CONCURRENT_DOWNLOADER_TASKS)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct ConcurrentSyncTasks(u16);
impl Default for ConcurrentSyncTasks {
fn default() -> Self {
ConcurrentSyncTasks(CONCURRENT_SYNC_TASKS)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct RequestRetries(u16);
impl Default for RequestRetries {
fn default() -> Self {
RequestRetries(REQUEST_RETRIES)
}
}
#[derive(Debug, Deserialize)]
#[serde(transparent)]
struct ConcurrentWriterTasks(u16);
impl Default for ConcurrentWriterTasks {
fn default() -> Self {
ConcurrentWriterTasks(CONCURRENT_WRITER_TASKS)
}
}
static CONFIG: OnceCell<Config> = OnceCell::new();
const EXPECT_GLOBAL_CONFIG: &str = "failed to parse config from environment";
impl Config {
pub fn global() -> &'static Config {
CONFIG.get_or_init(|| {
envy::prefixed("ESTHRI_")
.from_env::<Config>()
.expect(EXPECT_GLOBAL_CONFIG)
})
}
pub fn storage_class(&self) -> S3StorageClass {
self.storage_class.0.clone()
}
pub fn temp_dir_path(&self) -> Option<PathBuf> {
self.temp_dir_path.0.clone()
}
pub fn upload_part_size(&self) -> u64 {
self.upload_part_size.0
}
pub fn upload_read_size(&self) -> u64 {
self.upload_read_size.0
}
pub fn concurrent_upload_tasks(&self) -> usize {
self.concurrent_upload_tasks.0 as usize
}
pub fn concurrent_downloader_tasks(&self) -> usize {
self.concurrent_downloader_tasks.0 as usize
}
pub fn concurrent_sync_tasks(&self) -> usize {
self.concurrent_sync_tasks.0 as usize
}
pub fn request_retries(&self) -> usize {
self.request_retries.0 as usize
}
pub fn concurrent_writer_tasks(&self) -> usize {
self.concurrent_writer_tasks.0 as usize
}
}