use bevy::prelude::Resource; use figment::Figment; use figment::providers::{Env, Format, Serialized, Toml}; use serde::{Deserialize, Serialize}; #[derive(Debug, Resource, Serialize, Deserialize)] pub struct AppConfig { pub network: QuicConfig, pub simulation: SimulationConfig, pub observability: ObservabilityConfig, } #[derive(Debug, Serialize, Deserialize)] pub struct SimulationConfig { pub tick_rate_hz: u32, pub max_entities: usize, } #[derive(Debug, Serialize, Deserialize)] pub struct QuicConfig { pub server_port: u16, pub server_interface: String, pub server_cert: String, pub server_key: String, pub t1_capacity: usize, pub t2_capacity: usize, pub t3_capacity: usize, /// Bench-only knob. When > 0, the substrate spawns a synthetic T3 /// driver that issues toggling Relay commands to every connected device /// at the configured rate, exercising the real outbound code path. /// Off by default (0.0) in production. Override via env: /// `APP_NETWORK__SYNTHETIC_T3_RATE_HZ=100`. #[serde(default)] pub synthetic_t3_rate_hz: f64, } #[derive(Debug, Serialize, Deserialize)] pub struct ObservabilityConfig { /// When true, install the Prometheus exporter at startup. Disable for /// environments where the metrics port collides or scraping is undesired. pub metrics_enabled: bool, /// Bind address for the `/metrics` HTTP listener. pub metrics_listen: String, } impl Default for AppConfig { fn default() -> Self { Self { network : QuicConfig { server_port: 9000, server_interface: "0.0.0.0".to_string(), server_cert: "certs/server.crt".to_string(), server_key: "certs/server.key".to_string(), t1_capacity: 1024, t2_capacity: 512, t3_capacity: 256, synthetic_t3_rate_hz: 0.0, }, simulation: SimulationConfig { tick_rate_hz: 60, max_entities: 10000, }, observability: ObservabilityConfig { metrics_enabled: true, metrics_listen: "0.0.0.0:9100".to_string(), }, } } } impl AppConfig { pub fn load(config_file: &str) -> Result { Figment::new() .merge(Serialized::defaults(Self::default())) // compiled-in defaults .merge(Toml::file(config_file)) // config file // env overrides — `__` is the nesting separator so // `APP_NETWORK__SERVER_PORT=9001` overrides `network.server_port`. .merge(Env::prefixed("APP_").split("__")) .extract() } }