use crate::runtime::values::Value;
use crate::stdlib::crypto;
use std::collections::HashMap;
use std::env;
#[derive(Debug, Clone)]
pub struct ConfigManager {
pub environment: String,
pub encrypted_secrets: HashMap<String, String>,
pub config_cache: HashMap<String, Value>,
pub validation_rules: HashMap<String, ConfigValidationRule>,
}
#[derive(Debug, Clone)]
pub struct ConfigValidationRule {
pub required: bool,
pub default_value: Option<Value>,
pub validation_type: ValidationType,
pub min_length: Option<usize>,
pub max_length: Option<usize>,
pub allowed_values: Option<Vec<String>>,
pub regex_pattern: Option<String>,
}
#[derive(Debug, Clone)]
pub enum ValidationType {
String,
Integer,
Float,
Boolean,
URL,
Email,
PrivateKey,
PublicKey,
JWT,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct SecretConfig {
pub key: String,
pub encrypted_value: String,
pub encryption_algorithm: String,
pub created_at: i64,
pub expires_at: Option<i64>,
pub access_count: i64,
}
impl ConfigManager {
pub fn new() -> Self {
let environment = env::var("DIST_AGENT_ENV").unwrap_or_else(|_| "development".to_string());
Self {
environment,
encrypted_secrets: HashMap::new(),
config_cache: HashMap::new(),
validation_rules: HashMap::new(),
}
}
pub fn get_env(
key: &str,
validation_rule: Option<ConfigValidationRule>,
) -> Result<Value, String> {
let value =
env::var(key).map_err(|_| format!("Environment variable '{}' not found", key))?;
if let Some(rule) = validation_rule {
Self::validate_value(&value, &rule)?;
}
Ok(Value::String(value))
}
pub fn get_env_or_default(key: &str, default: Value) -> Value {
match env::var(key) {
Ok(value) => Value::String(value),
Err(_) => default,
}
}
pub fn get_required_env(key: &str) -> Result<Value, String> {
let rule = ConfigValidationRule {
required: true,
default_value: None,
validation_type: ValidationType::String,
min_length: Some(1),
max_length: None,
allowed_values: None,
regex_pattern: None,
};
Self::get_env(key, Some(rule))
}
pub fn store_secret(
&mut self,
key: &str,
value: &str,
encryption_key: &str,
) -> Result<(), String> {
let encrypted_value = crypto::encrypt_aes256(value, encryption_key)?;
let secret = SecretConfig {
key: key.to_string(),
encrypted_value,
encryption_algorithm: "AES-256-GCM".to_string(),
created_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64,
expires_at: None,
access_count: 0,
};
self.encrypted_secrets
.insert(key.to_string(), secret.encrypted_value);
Ok(())
}
pub fn get_secret(&mut self, key: &str, encryption_key: &str) -> Result<String, String> {
let encrypted_value = self
.encrypted_secrets
.get(key)
.ok_or_else(|| format!("Secret '{}' not found", key))?;
crypto::decrypt_aes256(encrypted_value, encryption_key)
}
pub fn get_database_config() -> Result<HashMap<String, Value>, String> {
let mut config = HashMap::new();
config.insert("host".to_string(), Self::get_required_env("DB_HOST")?);
config.insert(
"port".to_string(),
Self::get_env_or_default("DB_PORT", Value::Int(5432)),
);
config.insert("database".to_string(), Self::get_required_env("DB_NAME")?);
config.insert("username".to_string(), Self::get_required_env("DB_USER")?);
config.insert(
"password".to_string(),
Self::get_required_env("DB_PASSWORD")?,
);
config.insert(
"ssl_mode".to_string(),
Self::get_env_or_default("DB_SSL_MODE", Value::String("require".to_string())),
);
Ok(config)
}
pub fn get_api_config() -> Result<HashMap<String, Value>, String> {
let mut config = HashMap::new();
config.insert(
"base_url".to_string(),
Self::get_required_env("API_BASE_URL")?,
);
config.insert(
"timeout".to_string(),
Self::get_env_or_default("API_TIMEOUT", Value::Int(30000)),
);
config.insert(
"retry_count".to_string(),
Self::get_env_or_default("API_RETRY_COUNT", Value::Int(3)),
);
if let Ok(api_key) = Self::get_env("API_KEY", None) {
config.insert("api_key".to_string(), api_key);
}
Ok(config)
}
pub fn get_blockchain_config() -> Result<HashMap<String, Value>, String> {
let mut config = HashMap::new();
config.insert(
"rpc_url".to_string(),
Self::get_required_env("BLOCKCHAIN_RPC_URL")?,
);
config.insert(
"chain_id".to_string(),
Self::get_required_env("BLOCKCHAIN_CHAIN_ID")?,
);
config.insert(
"private_key".to_string(),
Self::get_required_env("BLOCKCHAIN_PRIVATE_KEY")?,
);
config.insert(
"gas_limit".to_string(),
Self::get_env_or_default("BLOCKCHAIN_GAS_LIMIT", Value::Int(21000)),
);
config.insert(
"gas_price".to_string(),
Self::get_env_or_default("BLOCKCHAIN_GAS_PRICE", Value::Float(20.0)),
);
Ok(config)
}
pub fn get_ai_config() -> Result<HashMap<String, Value>, String> {
let mut config = HashMap::new();
config.insert(
"model".to_string(),
Self::get_env_or_default("AI_MODEL", Value::String("gpt-4".to_string())),
);
config.insert(
"temperature".to_string(),
Self::get_env_or_default("AI_TEMPERATURE", Value::Float(0.7)),
);
config.insert(
"max_tokens".to_string(),
Self::get_env_or_default("AI_MAX_TOKENS", Value::Int(1000)),
);
if let Ok(api_key) = Self::get_env("AI_API_KEY", None) {
config.insert("api_key".to_string(), api_key);
}
Ok(config)
}
fn validate_value(value: &str, rule: &ConfigValidationRule) -> Result<(), String> {
if rule.required && value.is_empty() {
return Err("Value is required but empty".to_string());
}
if let Some(min_len) = rule.min_length {
if value.len() < min_len {
return Err(format!("Value too short, minimum length: {}", min_len));
}
}
if let Some(max_len) = rule.max_length {
if value.len() > max_len {
return Err(format!("Value too long, maximum length: {}", max_len));
}
}
if let Some(allowed) = &rule.allowed_values {
if !allowed.contains(&value.to_string()) {
return Err(format!(
"Value '{}' not in allowed values: {:?}",
value, allowed
));
}
}
match rule.validation_type {
ValidationType::Integer => {
value
.parse::<i64>()
.map_err(|_| "Value is not a valid integer".to_string())?;
}
ValidationType::Float => {
value
.parse::<f64>()
.map_err(|_| "Value is not a valid float".to_string())?;
}
ValidationType::Boolean => {
if !["true", "false", "1", "0"].contains(&value.to_lowercase().as_str()) {
return Err("Value is not a valid boolean".to_string());
}
}
ValidationType::URL => {
if !value.starts_with("http://") && !value.starts_with("https://") {
return Err("Value is not a valid URL".to_string());
}
}
ValidationType::Email => {
if !value.contains('@') || !value.contains('.') {
return Err("Value is not a valid email address".to_string());
}
}
ValidationType::PrivateKey => {
if !value.starts_with("0x") || value.len() != 66 {
return Err("Value is not a valid private key".to_string());
}
}
ValidationType::PublicKey => {
if !value.starts_with("0x") || value.len() != 130 {
return Err("Value is not a valid public key".to_string());
}
}
ValidationType::JWT => {
let parts: Vec<&str> = value.split('.').collect();
if parts.len() != 3 {
return Err("Value is not a valid JWT token".to_string());
}
}
_ => {} }
Ok(())
}
pub fn load_from_file(_path: &str) -> Result<HashMap<String, Value>, String> {
Ok(HashMap::new())
}
pub fn export_to_env(config: &HashMap<String, Value>) -> Result<(), String> {
for (key, value) in config {
let string_value = match value {
Value::String(s) => s.clone(),
Value::Int(i) => i.to_string(),
Value::Float(f) => f.to_string(),
Value::Bool(b) => b.to_string(),
_ => return Err(format!("Cannot export value type for key: {}", key)),
};
env::set_var(key, string_value);
}
Ok(())
}
}
impl Default for ConfigManager {
fn default() -> Self {
Self::new()
}
}
pub fn get_env(key: &str) -> Result<Value, String> {
ConfigManager::get_env(key, None)
}
pub fn get_env_or_default(key: &str, default: Value) -> Value {
ConfigManager::get_env_or_default(key, default)
}
pub fn get_required_env(key: &str) -> Result<Value, String> {
ConfigManager::get_required_env(key)
}
pub fn get_database_config() -> Result<HashMap<String, Value>, String> {
ConfigManager::get_database_config()
}
pub fn get_api_config() -> Result<HashMap<String, Value>, String> {
ConfigManager::get_api_config()
}
pub fn get_blockchain_config() -> Result<HashMap<String, Value>, String> {
ConfigManager::get_blockchain_config()
}
pub fn get_ai_config() -> Result<HashMap<String, Value>, String> {
ConfigManager::get_ai_config()
}