use crate::error::{to_env_var, ConfigError}; use config::{Config, Environment}; use serde::Deserialize; use std::net::SocketAddr; #[derive(Debug, Default, Deserialize)] pub struct Settings { #[serde(default = "default_host")] pub host: String, #[serde(default = "default_port")] pub port: u16, } impl Settings { pub fn socket_addr(&self) -> SocketAddr { format!("{}:{}", self.host, self.port) .parse() .expect("Failed to parse socket address") } pub fn new() -> Result { Self::load_and_validate() } fn load_and_validate() -> Result { // Start with default configuration let config = Config::builder() // Server defaults .set_default("host", default_host())? .set_default("port", default_port())? // Layer on the environment variables .add_source( Environment::with_prefix("GOOSE") .prefix_separator("_") .separator("__") .try_parsing(true), ) .build()?; // Try to deserialize the configuration let result: Result = config.try_deserialize(); // Handle missing field errors specially match result { Ok(settings) => Ok(settings), Err(err) => { tracing::debug!("Configuration error: {:?}", &err); // Handle both NotFound and missing field message variants let error_str = err.to_string(); if error_str.starts_with("missing field") { // Extract field name from error message "missing field `type`" let field = error_str .trim_start_matches("missing field `") .trim_end_matches("`"); let env_var = to_env_var(field); Err(ConfigError::MissingEnvVar { env_var }) } else if let config::ConfigError::NotFound(field) = &err { let env_var = to_env_var(field); Err(ConfigError::MissingEnvVar { env_var }) } else { Err(ConfigError::Other(err)) } } } } } fn default_host() -> String { "127.0.0.1".to_string() } fn default_port() -> u16 { 3000 } #[cfg(test)] mod tests { use super::*; #[test] fn test_socket_addr_conversion() { let server_settings = Settings { host: "127.0.0.1".to_string(), port: 3000, }; let addr = server_settings.socket_addr(); assert_eq!(addr.to_string(), "127.0.0.1:3000"); } }