From 55ca927c5c0dce2dcff08285a56e613e1c4c047c Mon Sep 17 00:00:00 2001 From: evdokimovs <49490279+evdokimovs@users.noreply.github.com> Date: Wed, 22 May 2019 15:14:25 +0300 Subject: [PATCH] Refactor config values merging (#24) Additionally: - fix config tests --- config.default.toml | 5 --- config.toml | 20 ++++++++++++ src/conf/mod.rs | 75 ++++++++++++++++----------------------------- src/conf/rpc.rs | 1 + src/conf/server.rs | 4 +++ 5 files changed, 52 insertions(+), 53 deletions(-) delete mode 100644 config.default.toml diff --git a/config.default.toml b/config.default.toml deleted file mode 100644 index ebf1a99ce..000000000 --- a/config.default.toml +++ /dev/null @@ -1,5 +0,0 @@ -[server] -# Timeout for websocket session to wait message from [`Web Client`]. -# -# Default: -# idle_timeout = "10s" diff --git a/config.toml b/config.toml index e8c24fe68..500fa0dd8 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,26 @@ [server] +# IP address to bind HTTP server to. +# +# Default: +# bind_ip = "0.0.0.0" + +# Port to bind HTTP server to. +# +# Default: +# bind_port = 8080 + + + + +[rpc] # Duration, after which remote RPC client will be considered idle if no # heartbeat messages received. # # Default: # idle_timeout = "10s" + +# Duration, after which the server deletes the client session if +# the remote RPC client does not reconnect after it is idle. +# +# Default: +# reconnect_timeout = "10s" diff --git a/src/conf/mod.rs b/src/conf/mod.rs index cd5a8a925..c509f56a9 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -5,16 +5,11 @@ pub mod server; use std::env; -use config::{ - Config, ConfigError, Environment, File, FileFormat, Source, Value, -}; +use config::{Config, Environment, File}; use failure::Error; use serde::{Deserialize, Serialize}; -pub use self::rpc::Rpc; -pub use self::server::Server; - -use std::collections::HashMap; +pub use self::{rpc::Rpc, server::Server}; /// CLI argument that is responsible for holding application configuration /// file path. @@ -25,25 +20,13 @@ static APP_CONF_PATH_ENV_VAR_NAME: &str = "MEDEA_CONF"; /// Holds application config. #[derive(Clone, Debug, Deserialize, Serialize, Default)] +#[serde(default)] pub struct Conf { - /// HTTP server settings. - pub rpc: rpc::Rpc, /// RPC connection settings. - pub server: server::Server, -} - -/// Allows merging [`Conf`] into [`config::Config`]. -// TODO: Remove after the following issue is resolved: -// https://github.com/mehcode/config-rs/issues/60#issuecomment-443241600 -impl Source for Conf { - fn clone_into_box(&self) -> Box { - Box::new((*self).clone()) - } + pub rpc: rpc::Rpc, - fn collect(&self) -> Result, ConfigError> { - let serialized = toml::to_string(self).unwrap(); - File::from_str(serialized.as_str(), FileFormat::Toml).collect() - } + /// HTTP server settings. + pub server: server::Server, } impl Conf { @@ -54,10 +37,7 @@ impl Conf { /// parameter or environment variable; /// - environment variables. pub fn parse() -> Result { - // TODO: use Config::try_from(&Self::default()) when the issue is fixed: - // https://github.com/mehcode/config-rs/issues/60 let mut cfg = Config::new(); - cfg.merge(Self::default())?; if let Some(path) = get_conf_file_name(env::args()) { cfg.merge(File::with_name(&path))?; @@ -65,8 +45,7 @@ impl Conf { cfg.merge(Environment::with_prefix("MEDEA").separator("."))?; - let s: Self = cfg.try_into()?; - Ok(s) + Ok(cfg.try_into()?) } } @@ -91,17 +70,22 @@ where } #[cfg(test)] -mod get_conf_file_name_spec { +mod tests { + use serial_test_derive::serial; + use std::{fs, time::Duration}; + use super::*; #[test] - fn none_if_nothing_is_set() { + #[serial] + fn get_conf_file_name_spec_none_if_nothing_is_set() { env::remove_var(APP_CONF_PATH_ENV_VAR_NAME); assert_eq!(get_conf_file_name(vec![]), None); } #[test] - fn none_if_empty() { + #[serial] + fn get_conf_file_name_spec_none_if_empty() { env::set_var(APP_CONF_PATH_ENV_VAR_NAME, "env_path"); assert_eq!( get_conf_file_name(vec![ @@ -110,16 +94,20 @@ mod get_conf_file_name_spec { ]), None, ); + env::remove_var(APP_CONF_PATH_ENV_VAR_NAME); } #[test] - fn env_if_set() { + #[serial] + fn get_conf_file_name_spec_env_if_set() { env::set_var(APP_CONF_PATH_ENV_VAR_NAME, "env_path"); assert_eq!(get_conf_file_name(vec![]), Some("env_path".to_owned())); + env::remove_var(APP_CONF_PATH_ENV_VAR_NAME); } #[test] - fn arg_if_set() { + #[serial] + fn get_conf_file_name_spec_arg_if_set() { env::remove_var(APP_CONF_PATH_ENV_VAR_NAME); assert_eq!( get_conf_file_name(vec![ @@ -131,7 +119,8 @@ mod get_conf_file_name_spec { } #[test] - fn arg_is_prioritized() { + #[serial] + fn get_conf_file_name_spec_arg_is_prioritized() { env::set_var(APP_CONF_PATH_ENV_VAR_NAME, "env_path"); assert_eq!( get_conf_file_name(vec![ @@ -140,20 +129,12 @@ mod get_conf_file_name_spec { ]), Some("arg_path".to_owned()), ); + env::remove_var(APP_CONF_PATH_ENV_VAR_NAME); } -} - -#[cfg(test)] -mod conf_parse_spec { - use std::{fs, time::Duration}; - - use serial_test_derive::serial; - - use super::*; #[test] #[serial] - fn file_overrides_defaults() { + fn conf_parse_spec_file_overrides_defaults() { let defaults = Conf::default(); let test_config_file_path = "test_config.toml"; @@ -170,11 +151,9 @@ mod conf_parse_spec { assert_ne!(new_config.rpc.idle_timeout, defaults.rpc.idle_timeout); } - // TODO: This test seems to pollute environment and might - // fail from time to time. #[test] #[serial] - fn env_overrides_defaults() { + fn conf_parse_spec_env_overrides_defaults() { let defaults = Conf::default(); env::set_var("MEDEA_RPC.IDLE_TIMEOUT", "46s"); @@ -187,7 +166,7 @@ mod conf_parse_spec { #[test] #[serial] - fn env_overrides_file() { + fn conf_parse_spec_env_overrides_file() { let test_config_file_path = "test_config.toml"; let data = format!("[rpc]\nidle_timeout = \"47s\""); diff --git a/src/conf/rpc.rs b/src/conf/rpc.rs index e3fdda999..097b09b89 100644 --- a/src/conf/rpc.rs +++ b/src/conf/rpc.rs @@ -6,6 +6,7 @@ use std::time::Duration; /// RPC connection settings. #[derive(Clone, Debug, Deserialize, Serialize, SmartDefault)] +#[serde(default)] pub struct Rpc { /// Duration, after which remote RPC client will be considered idle if no /// heartbeat messages received. Defaults to `10s`. diff --git a/src/conf/server.rs b/src/conf/server.rs index 7cf8d6764..bd89c3965 100644 --- a/src/conf/server.rs +++ b/src/conf/server.rs @@ -7,6 +7,7 @@ use smart_default::*; /// HTTP server settings. #[derive(Clone, Debug, Deserialize, Serialize, SmartDefault)] +#[serde(default)] pub struct Server { /// IP address to bind HTTP server to. Defaults to `0.0.0.0`. #[default(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))] @@ -48,6 +49,9 @@ mod server_spec { let env_conf = Conf::parse().unwrap(); + env::remove_var("MEDEA_SERVER.BIND_IP"); + env::remove_var("MEDEA_SERVER.BIND_PORT"); + assert_ne!(default_conf.server.bind_ip, env_conf.server.bind_ip); assert_ne!(default_conf.server.bind_port, env_conf.server.bind_port);