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, /// Flink market data endpoint (XSUB - relay subscribes to Flink XPUB) #[serde(default = "default_flink_market_data_endpoint")] pub flink_market_data_endpoint: String, /// Flink request endpoint (PUSH - relay forwards client requests to Flink PULL) /// Flink's IngestorBroker binds a PULL socket on port 5566 #[serde(default = "default_flink_request_endpoint")] pub flink_request_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_flink_market_data_endpoint() -> String { "tcp://flink-jobmanager:5558".to_string() } fn default_flink_request_endpoint() -> String { "tcp://flink-jobmanager:5566".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(), flink_market_data_endpoint: default_flink_market_data_endpoint(), flink_request_endpoint: default_flink_request_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()) } } }