Files
yggman/src/config/mod.rs

178 lines
5.4 KiB
Rust
Raw Normal View History

2025-08-25 21:48:59 +03:00
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use arc_swap::ArcSwap;
use std::sync::Arc;
use crate::cli::{CliArgs, EnvConfig};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
#[serde(default)]
pub server: ServerConfig,
#[serde(default)]
pub database: DatabaseConfig,
#[serde(default)]
pub nodes: NodesConfig,
#[serde(default)]
pub modules: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub bind_address: String,
pub port: u16,
pub workers: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub connect_timeout: u64,
pub acquire_timeout: u64,
pub idle_timeout: u64,
pub max_lifetime: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodesConfig {
pub max_peers_per_node: usize,
pub topology_update_interval: u64,
pub default_listen_endpoints: Vec<String>,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
bind_address: "127.0.0.1".to_string(),
port: 8080,
workers: 4,
}
}
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
url: "sqlite://yggman.db".to_string(),
max_connections: 10,
connect_timeout: 30,
acquire_timeout: 30,
idle_timeout: 600,
max_lifetime: 3600,
}
}
}
impl Default for NodesConfig {
fn default() -> Self {
Self {
max_peers_per_node: 3,
topology_update_interval: 60,
default_listen_endpoints: vec!["tcp://0.0.0.0:9001".to_string()],
}
}
}
impl Default for AppConfig {
fn default() -> Self {
Self {
server: ServerConfig::default(),
database: DatabaseConfig::default(),
nodes: NodesConfig::default(),
modules: HashMap::new(),
}
}
}
pub struct ConfigManager {
config: Arc<ArcSwap<AppConfig>>,
}
impl ConfigManager {
pub fn new(config: AppConfig) -> Self {
Self {
config: Arc::new(ArcSwap::from_pointee(config)),
}
}
pub fn get(&self) -> Arc<AppConfig> {
self.config.load_full()
}
2025-08-26 13:08:43 +03:00
pub fn update_listen_template(&self, new_template: Vec<String>) {
let current = self.config.load_full();
let mut new_config = current.as_ref().clone();
new_config.nodes.default_listen_endpoints = new_template;
self.config.store(Arc::new(new_config));
tracing::info!("Listen template updated in memory");
}
2025-08-25 21:48:59 +03:00
/// Load configuration from multiple sources with precedence:
/// CLI args > Environment variables > Config file > Defaults
pub fn load_merged_config(cli_args: &CliArgs, env_config: &EnvConfig) -> Result<AppConfig, crate::error::AppError> {
// Start with default config
let mut config = AppConfig::default();
// Load from config file if it exists
if std::path::Path::new(&cli_args.config).exists() {
let content = std::fs::read_to_string(&cli_args.config)
.map_err(|e| crate::error::AppError::Config(format!("Failed to read config file: {}", e)))?;
config = toml::from_str(&content)
.map_err(|e| crate::error::AppError::Config(format!("Failed to parse config file: {}", e)))?;
}
// Override with environment variables
if let Some(bind_address) = &env_config.server.bind_address {
config.server.bind_address = bind_address.clone();
}
if let Some(port) = env_config.server.port {
config.server.port = port;
}
if let Some(workers) = env_config.server.workers {
config.server.workers = workers;
}
if let Some(db_url) = &env_config.database.url {
config.database.url = db_url.clone();
}
if let Some(max_connections) = env_config.database.max_connections {
config.database.max_connections = max_connections;
}
if let Some(max_peers) = env_config.nodes.max_peers_per_node {
config.nodes.max_peers_per_node = max_peers;
}
if let Some(topology_update) = env_config.nodes.topology_update_interval {
config.nodes.topology_update_interval = topology_update;
}
// Override with CLI arguments (highest priority)
if let Some(bind_address) = &cli_args.bind_address {
config.server.bind_address = bind_address.clone();
}
if let Some(port) = cli_args.port {
config.server.port = port;
}
if let Some(workers) = cli_args.workers {
config.server.workers = workers;
}
if let Some(db_url) = &cli_args.database_url {
config.database.url = db_url.clone();
}
if let Some(max_connections) = cli_args.max_db_connections {
config.database.max_connections = max_connections;
}
if let Some(max_peers) = cli_args.max_peers {
config.nodes.max_peers_per_node = max_peers;
}
if let Some(topology_update) = cli_args.topology_update_interval {
config.nodes.topology_update_interval = topology_update;
}
Ok(config)
}
}