use serde::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
#[cfg(feature = "aws-backend")]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynamoDbConfig {
pub region: String,
pub tables: ExplicitTables,
pub auto_create: bool,
#[serde(default)]
pub user_id: Option<String>,
#[serde(default)]
pub file_storage_bucket: Option<String>,
}
#[cfg(feature = "aws-backend")]
impl DynamoDbConfig {
pub fn from_env() -> Result<Self, ConfigError> {
let region = env::var("DATAFOLD_DYNAMODB_REGION")
.map_err(|_| ConfigError::MissingVariable("DATAFOLD_DYNAMODB_REGION".to_string()))?;
let table_prefix = env::var("DATAFOLD_DYNAMODB_TABLE_PREFIX")
.or_else(|_| env::var("DATAFOLD_DYNAMODB_TABLE")) .map_err(|_| {
ConfigError::MissingVariable("DATAFOLD_DYNAMODB_TABLE_PREFIX".to_string())
})?;
let tables = ExplicitTables::from_prefix(&table_prefix);
Ok(Self {
region,
tables,
auto_create: true,
user_id: env::var("DATAFOLD_DYNAMODB_USER_ID").ok(),
file_storage_bucket: env::var("DATAFOLD_S3_FILE_STORAGE_BUCKET").ok(),
})
}
}
#[cfg(feature = "aws-backend")]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ExplicitTables {
pub main: String,
pub metadata: String,
pub permissions: String,
pub transforms: String,
pub orchestrator: String,
pub schema_states: String,
pub schemas: String,
pub public_keys: String,
pub transform_queue: String,
pub native_index: String,
pub process: String,
pub logs: String,
}
#[cfg(feature = "aws-backend")]
impl ExplicitTables {
pub fn from_prefix(prefix: &str) -> Self {
Self {
main: format!("{}-main", prefix),
metadata: format!("{}-metadata", prefix),
permissions: format!("{}-node_id_schema_permissions", prefix),
transforms: format!("{}-transforms", prefix),
orchestrator: format!("{}-orchestrator_state", prefix),
schema_states: format!("{}-schema_states", prefix),
schemas: format!("{}-schemas", prefix),
public_keys: format!("{}-public_keys", prefix),
transform_queue: format!("{}-transform_queue_tree", prefix),
native_index: format!("{}-native_index", prefix),
process: format!("{}-process", prefix),
logs: format!("{}-logs", prefix),
}
}
}
#[derive(Debug)]
pub enum ConfigError {
MissingVariable(String),
InvalidValue(String),
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConfigError::MissingVariable(var) => write!(f, "Missing environment variable: {}", var),
ConfigError::InvalidValue(msg) => write!(f, "Invalid configuration value: {}", msg),
}
}
}
impl std::error::Error for ConfigError {}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum DatabaseConfig {
#[serde(rename = "local")]
Local {
path: PathBuf,
},
#[cfg(feature = "aws-backend")]
#[serde(rename = "dynamodb")]
DynamoDb(DynamoDbConfig),
}
impl Default for DatabaseConfig {
fn default() -> Self {
DatabaseConfig::Local {
path: PathBuf::from("data"),
}
}
}
#[derive(Debug, Clone)]
pub enum UploadStorageConfig {
Local { path: PathBuf },
}
impl Default for UploadStorageConfig {
fn default() -> Self {
UploadStorageConfig::Local {
path: PathBuf::from("data/uploads"),
}
}
}
impl UploadStorageConfig {
pub fn from_env() -> Result<Self, ConfigError> {
let mode = env::var("DATAFOLD_UPLOAD_STORAGE_MODE").unwrap_or_else(|_| "local".to_string());
match mode.as_str() {
"local" => {
let path = env::var("DATAFOLD_UPLOAD_PATH")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("data/uploads"));
Ok(UploadStorageConfig::Local { path })
}
_ => Err(ConfigError::InvalidValue(format!(
"Invalid DATAFOLD_UPLOAD_STORAGE_MODE: {}. Must be 'local' or 's3'",
mode
))),
}
}
}
impl DatabaseConfig {
pub fn from_env() -> Result<Self, ConfigError> {
let mode = env::var("DATAFOLD_STORAGE_MODE").unwrap_or_else(|_| "local".to_string());
match mode.to_lowercase().as_str() {
"local" => {
let path = env::var("DATAFOLD_STORAGE_PATH")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("data"));
Ok(DatabaseConfig::Local { path })
}
#[cfg(feature = "aws-backend")]
"dynamodb" => {
let config = DynamoDbConfig::from_env()?;
Ok(DatabaseConfig::DynamoDb(config))
}
_ => Err(ConfigError::InvalidValue(format!(
"Invalid DATAFOLD_STORAGE_MODE: '{}'. Must be 'local' or 's3'{}",
mode,
if cfg!(feature = "aws-backend") {
", 'dynamodb'"
} else {
""
}
))),
}
}
}