use serde::Deserialize;
use std::fs;
use std::path::Path;
#[derive(Debug, Deserialize)]
pub struct AppConfig {
#[serde(default)]
pub server: ServerConfig,
pub database: DatabaseConfig,
#[serde(default)]
pub logging: LoggingConfig,
#[serde(default)]
pub cors: CorsConfig,
}
#[derive(Debug, Deserialize)]
pub struct ServerConfig {
#[serde(default = "default_host")]
pub host: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default = "default_worker_pool_size")]
pub worker_pool_size: usize,
#[serde(default = "default_read_buffer_size")]
pub read_buffer_size: usize,
#[serde(default = "default_tcp_nodelay")]
pub tcp_nodelay: bool,
#[serde(default = "default_keep_alive_timeout")]
pub keep_alive_timeout: u64,
#[serde(default = "default_static_dir")]
pub static_dir: String,
#[serde(default = "default_max_connections")]
pub max_connections: usize,
#[serde(default = "default_connection_timeout")]
pub connection_timeout: u64,
}
#[derive(Debug, Deserialize)]
pub struct DatabaseConfig {
#[serde(default = "default_host")]
pub host: String,
#[serde(default = "default_port")]
pub port: u16,
#[serde(default = "default_username")]
pub username: String,
#[serde(default = "default_password")]
pub password: String,
#[serde(default = "default_database")]
pub database: String,
#[serde(default = "default_max_size")]
pub max_size: usize,
}
#[derive(Debug, Deserialize)]
pub struct LoggingConfig {
#[serde(default = "default_log_level")]
pub level: String,
#[serde(default = "default_log_output")]
pub output: String,
#[serde(default)]
pub file: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct CorsConfig {
#[serde(default = "default_allowed_origins")]
pub allowed_origins: Vec<String>,
#[serde(default = "default_allowed_methods")]
pub allowed_methods: Vec<String>,
#[serde(default = "default_allowed_headers")]
pub allowed_headers: Vec<String>,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
server: ServerConfig::default(),
database: DatabaseConfig::default(),
logging: LoggingConfig::default(),
cors: CorsConfig::default(),
}
}
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: default_host(),
port: default_port(),
worker_pool_size: default_worker_pool_size(),
read_buffer_size: default_read_buffer_size(),
tcp_nodelay: default_tcp_nodelay(),
keep_alive_timeout: default_keep_alive_timeout(),
static_dir: default_static_dir(),
max_connections: default_max_connections(),
connection_timeout: default_connection_timeout(),
}
}
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
host: default_host(),
port: default_port(),
username: default_username(),
password: default_password(),
database: default_database(),
max_size: default_max_size(),
}
}
}
impl Default for LoggingConfig {
fn default() -> Self {
Self {
level: default_log_level(),
output: default_log_output(),
file: None,
}
}
}
impl Default for CorsConfig {
fn default() -> Self {
Self {
allowed_origins: default_allowed_origins(),
allowed_methods: default_allowed_methods(),
allowed_headers: default_allowed_headers(),
}
}
}
impl ServerConfig {
pub fn addr(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
impl AppConfig {
pub fn load(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let config_path = Path::new(path);
if config_path.exists() {
let content = fs::read_to_string(config_path)?;
let config: AppConfig = toml::from_str(&content)?;
Ok(config)
} else {
log::warn!("Config file '{}' not found, using defaults", path);
Ok(AppConfig::default())
}
}
}
fn default_host() -> String {
"127.0.0.1".to_string()
}
fn default_port() -> u16 {
9030
}
fn default_worker_pool_size() -> usize {
num_cpus::get()
}
fn default_read_buffer_size() -> usize {
8192
}
fn default_tcp_nodelay() -> bool {
true
}
fn default_keep_alive_timeout() -> u64 {
5
}
fn default_static_dir() -> String {
"public".to_string()
}
fn default_max_connections() -> usize {
0
}
fn default_connection_timeout() -> u64 {
30
}
fn default_username() -> String {
"root".to_string()
}
fn default_password() -> String {
"".to_string()
}
fn default_database() -> String {
"grweb_db".to_string()
}
fn default_max_size() -> usize {
5
}
fn default_log_level() -> String {
"info".to_string()
}
fn default_log_output() -> String {
"console".to_string()
}
fn default_allowed_origins() -> Vec<String> {
vec!["*".to_string()]
}
fn default_allowed_methods() -> Vec<String> {
vec![
"GET".to_string(),
"POST".to_string(),
"PUT".to_string(),
"DELETE".to_string(),
"OPTIONS".to_string(),
]
}
fn default_allowed_headers() -> Vec<String> {
vec!["Content-Type".to_string()]
}