use anyhow::Result; use serde::{Deserialize, Serialize}; use std::fs; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { /// Bind address for client-facing sockets #[serde(default = "default_bind_address")] pub bind_address: String, /// Client request port (ROUTER - receives client requests) #[serde(default = "default_client_request_port")] pub client_request_port: u16, /// Market data publication port (XPUB - clients subscribe here) #[serde(default = "default_market_data_pub_port")] pub market_data_pub_port: u16, /// Ingestor work queue port (PUB - publish work with exchange prefix) #[serde(default = "default_ingestor_work_port")] pub ingestor_work_port: u16, /// Ingestor response port (ROUTER - receives responses from ingestors) #[serde(default = "default_ingestor_response_port")] pub ingestor_response_port: u16, /// Flink market data endpoint (XSUB - relay subscribes to Flink) #[serde(default = "default_flink_market_data_endpoint")] pub flink_market_data_endpoint: String, /// Request timeout in seconds #[serde(default = "default_request_timeout_secs")] pub request_timeout_secs: u64, /// High water mark for sockets #[serde(default = "default_hwm")] pub high_water_mark: i32, } fn default_bind_address() -> String { "tcp://*".to_string() } fn default_client_request_port() -> u16 { 5559 } fn default_market_data_pub_port() -> u16 { 5558 } fn default_ingestor_work_port() -> u16 { 5555 } fn default_ingestor_response_port() -> u16 { 5556 } fn default_flink_market_data_endpoint() -> String { "tcp://flink-jobmanager:5557".to_string() } fn default_request_timeout_secs() -> u64 { 30 } fn default_hwm() -> i32 { 10000 } impl Default for Config { fn default() -> Self { Self { bind_address: default_bind_address(), client_request_port: default_client_request_port(), market_data_pub_port: default_market_data_pub_port(), ingestor_work_port: default_ingestor_work_port(), ingestor_response_port: default_ingestor_response_port(), flink_market_data_endpoint: default_flink_market_data_endpoint(), request_timeout_secs: default_request_timeout_secs(), high_water_mark: default_hwm(), } } } impl Config { pub fn from_file(path: &str) -> Result { let contents = fs::read_to_string(path)?; let config: Config = serde_yaml::from_str(&contents)?; Ok(config) } pub fn from_env() -> Result { let config_path = std::env::var("CONFIG_PATH") .unwrap_or_else(|_| "/config/config.yaml".to_string()); if std::path::Path::new(&config_path).exists() { Self::from_file(&config_path) } else { Ok(Self::default()) } } }