use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::net::IpAddr;
use std::env;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub host: IpAddr,
pub port: u16,
pub log_level: String,
pub cors: CorsConfig,
pub limits: LimitsConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorsConfig {
pub enabled: bool,
pub origins: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LimitsConfig {
pub max_request_size_mb: usize,
pub request_timeout_seconds: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PoolConfig {
pub max_connections: usize,
pub min_idle: usize,
pub queue_timeout_ms: u64,
pub max_lifetime_minutes: u64,
pub enable_queue: bool,
pub worker_threads: usize,
}
impl Default for PoolConfig {
fn default() -> Self {
Self {
max_connections: 30,
min_idle: 5,
queue_timeout_ms: 30000,
max_lifetime_minutes: 30,
enable_queue: true,
worker_threads: 10,
}
}
}
impl PoolConfig {
pub fn validate(&self) -> Result<(), String> {
if self.max_connections == 0 {
return Err("max_connections deve ser maior que 0".to_string());
}
if self.max_connections > 750 {
return Err("max_connections não pode exceder 750".to_string());
}
if self.min_idle > self.max_connections {
return Err("min_idle não pode ser maior que max_connections".to_string());
}
if self.worker_threads == 0 {
return Err("worker_threads deve ser maior que 0".to_string());
}
Ok(())
}
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "127.0.0.1".parse().unwrap(),
port: 8560,
log_level: "info".to_string(),
cors: CorsConfig {
enabled: true,
origins: vec!["*".to_string()],
},
limits: LimitsConfig {
max_request_size_mb: 10,
request_timeout_seconds: 60,
},
}
}
}
pub async fn load_server_config() -> Result<ServerConfig> {
let config_dir = env::var("CONFIG_DIR").unwrap_or_else(|_| "config".to_string());
let config_path = PathBuf::from(config_dir).join("server.json");
if let Ok(content) = tokio::fs::read_to_string(&config_path).await
&& let Ok(config) = serde_json::from_str::<ServerConfig>(&content) {
return Ok(config);
}
Ok(ServerConfig::default())
}
pub async fn load_pool_config() -> Result<PoolConfig> {
let config_dir = env::var("CONFIG_DIR").unwrap_or_else(|_| "config".to_string());
let config_path = PathBuf::from(config_dir).join("pool.json");
tracing::info!("Loading pool configuration from: {}", config_path.display());
if let Ok(content) = tokio::fs::read_to_string(&config_path).await {
match serde_json::from_str::<PoolConfig>(&content) {
Ok(config) => {
if let Err(e) = config.validate() {
tracing::error!("Invalid pool configuration: {}", e);
tracing::warn!("Using default pool configuration");
return Ok(PoolConfig::default());
}
tracing::info!(
"Pool configuration loaded: max_connections={}, min_idle={}, enable_queue={}",
config.max_connections,
config.min_idle,
config.enable_queue
);
return Ok(config);
}
Err(e) => {
tracing::error!("Failed to parse pool.json: {}", e);
tracing::warn!("Using default pool configuration");
}
}
} else {
tracing::warn!("pool.json not found, using default configuration");
}
Ok(PoolConfig::default())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_server_config_default() {
let config = ServerConfig::default();
assert_eq!(config.host.to_string(), "127.0.0.1");
assert_eq!(config.port, 8560);
assert_eq!(config.log_level, "info");
assert!(config.cors.enabled);
assert_eq!(config.limits.max_request_size_mb, 10);
}
#[test]
fn test_pool_config_default() {
let config = PoolConfig::default();
assert_eq!(config.max_connections, 30);
assert_eq!(config.min_idle, 5);
assert_eq!(config.queue_timeout_ms, 30000);
assert_eq!(config.max_lifetime_minutes, 30);
assert!(config.enable_queue);
assert_eq!(config.worker_threads, 10);
}
#[test]
fn test_pool_config_validate_valid() {
let config = PoolConfig::default();
assert!(config.validate().is_ok());
}
#[test]
fn test_pool_config_validate_zero_max_connections() {
let config = PoolConfig {
max_connections: 0,
..Default::default()
};
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("max_connections"));
}
#[test]
fn test_pool_config_validate_too_many_connections() {
let config = PoolConfig {
max_connections: 1000,
..Default::default()
};
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("750"));
}
#[test]
fn test_pool_config_validate_min_idle_greater_than_max() {
let config = PoolConfig {
max_connections: 10,
min_idle: 20,
..Default::default()
};
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("min_idle"));
}
#[test]
fn test_pool_config_validate_zero_worker_threads() {
let config = PoolConfig {
worker_threads: 0,
..Default::default()
};
let result = config.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("worker_threads"));
}
#[test]
fn test_pool_config_serialization() {
let config = PoolConfig::default();
let json = serde_json::to_string(&config).unwrap();
assert!(json.contains("max_connections"));
assert!(json.contains("30"));
}
#[test]
fn test_pool_config_deserialization() {
let json_str = r#"{
"max_connections": 50,
"min_idle": 10,
"queue_timeout_ms": 60000,
"max_lifetime_minutes": 60,
"enable_queue": false,
"worker_threads": 20
}"#;
let config: PoolConfig = serde_json::from_str(json_str).unwrap();
assert_eq!(config.max_connections, 50);
assert_eq!(config.min_idle, 10);
assert_eq!(config.queue_timeout_ms, 60000);
assert_eq!(config.max_lifetime_minutes, 60);
assert!(!config.enable_queue);
assert_eq!(config.worker_threads, 20);
}
#[test]
fn test_server_config_serialization() {
let config = ServerConfig::default();
let json = serde_json::to_string(&config).unwrap();
assert!(json.contains("127.0.0.1"));
assert!(json.contains("8560"));
assert!(json.contains("info"));
}
#[test]
fn test_cors_config_deserialization() {
let json_str = r#"{
"enabled": true,
"origins": ["https://example.com", "https://app.example.com"]
}"#;
let config: CorsConfig = serde_json::from_str(json_str).unwrap();
assert!(config.enabled);
assert_eq!(config.origins.len(), 2);
assert_eq!(config.origins[0], "https://example.com");
}
#[test]
fn test_limits_config_deserialization() {
let json_str = r#"{
"max_request_size_mb": 50,
"request_timeout_seconds": 120
}"#;
let config: LimitsConfig = serde_json::from_str(json_str).unwrap();
assert_eq!(config.max_request_size_mb, 50);
assert_eq!(config.request_timeout_seconds, 120);
}
}