diff --git a/itconfig/src/lib.rs b/itconfig/src/lib.rs index 7825335..ba297dd 100644 --- a/itconfig/src/lib.rs +++ b/itconfig/src/lib.rs @@ -308,6 +308,29 @@ macro_rules! __itconfig_parse_variables { } }; + // Find concatenated variable + ( + tokens = [ + $(#$meta:tt)* + $name:ident < ($($inner:tt)+), + $($rest:tt)* + ], + $($args:tt)* + ) => { + __itconfig_parse_variables! { + current_variable = { + unparsed_meta = [$(#$meta)*], + meta = [], + unparsed_concat = [$($inner)+], + concat = [], + name = $name, + ty = String, + }, + tokens = [$($rest)*], + $($args)* + } + }; + // Find variable ( tokens = [ @@ -321,6 +344,8 @@ macro_rules! __itconfig_parse_variables { current_variable = { unparsed_meta = [$(#$meta)*], meta = [], + unparsed_concat = [], + concat = [], name = $name, ty = $ty, $(default = $default,)? @@ -338,6 +363,8 @@ macro_rules! __itconfig_parse_variables { $($rest:tt)* ], meta = $meta:tt, + unparsed_concat = $unparsed_concat:tt, + concat = $concat:tt, name = $name:ident, $($current_variable:tt)* }, @@ -347,6 +374,8 @@ macro_rules! __itconfig_parse_variables { current_variable = { unparsed_meta = [$($rest)*], meta = $meta, + unparsed_concat = $unparsed_concat, + concat = $concat, name = $name, env_name = $env_name, $($current_variable)* @@ -363,7 +392,6 @@ macro_rules! __itconfig_parse_variables { $($rest:tt)* ], meta = [$(#$meta:tt,)*], - name = $name:ident, $($current_variable:tt)* }, $($args:tt)* @@ -372,7 +400,32 @@ macro_rules! __itconfig_parse_variables { current_variable = { unparsed_meta = [$($rest)*], meta = [$(#$meta,)* #$stranger_meta,], - name = $name, + $($current_variable)* + }, + $($args)* + } + }; + + // Parse concat params + ( + current_variable = { + unparsed_meta = $unparsed_meta:tt, + meta = $meta:tt, + unparsed_concat = [ + $concat_param:tt, + $($rest:tt)* + ], + concat = [$($concat:expr,)*], + $($current_variable:tt)* + }, + $($args:tt)* + ) => { + __itconfig_parse_variables! { + current_variable = { + unparsed_meta = $unparsed_meta, + meta = $meta, + unparsed_concat = [$($rest)*], + concat = [$($concat,)* __itconfig_concat_param!($concat_param),], $($current_variable)* }, $($args)* @@ -383,6 +436,8 @@ macro_rules! __itconfig_parse_variables { ( current_variable = { unparsed_meta = [], + meta = $meta:tt, + unparsed_concat = [], $($current_variable:tt)* }, tokens = $tokens:tt, @@ -391,7 +446,7 @@ macro_rules! __itconfig_parse_variables { ) => { __itconfig_parse_variables! { tokens = $tokens, - variables = [$($variables,)* { $($current_variable)* },], + variables = [$($variables,)* { meta = $meta, $($current_variable)* },], $($args)* } }; @@ -445,12 +500,14 @@ macro_rules! __itconfig_impl { ( variables = [$({ meta = $var_meta:tt, + concat = $var_concat:tt, name = $var_name:ident, $($variable:tt)* },)*], namespaces = [$({ variables = [$({ meta = $ns_var_meta:tt, + concat = $ns_var_concat:tt, name = $ns_var_name:ident, $($ns_variables:tt)* },)*], @@ -464,10 +521,17 @@ macro_rules! __itconfig_impl { ) => { pub mod $mod_name { #![allow(non_snake_case)] + use std::env; + use itconfig::EnvValue; + $( pub mod $ns_name { + use std::env; + use itconfig::EnvValue; + $(__itconfig_variable! { meta = $ns_var_meta, + concat = $ns_var_concat, name = $ns_var_name, env_prefix = $ns_env_prefix, $($ns_variables)* @@ -483,6 +547,7 @@ macro_rules! __itconfig_impl { $(__itconfig_variable! { meta = $var_meta, + concat = $var_concat, name = $var_name, env_prefix = $env_prefix, $($variable)* @@ -497,12 +562,22 @@ macro_rules! __itconfig_impl { } +#[macro_export] +#[doc(hidden)] +macro_rules! __itconfig_concat_param { + ($env:ident) => ( env_or!(stringify!($env).to_uppercase()) ); + + ($str:expr) => ( $str.to_string() ); +} + + #[macro_export] #[doc(hidden)] macro_rules! __itconfig_variable { // Set default env name ( meta = $meta:tt, + concat = $concat:tt, name = $name:ident, env_prefix = $env_prefix:expr, ty = $ty:ty, @@ -510,6 +585,7 @@ macro_rules! __itconfig_variable { ) => { __itconfig_variable! { meta = $meta, + concat = $concat, name = $name, env_prefix = $env_prefix, env_name = concat!($env_prefix, stringify!($name)).to_uppercase(), @@ -518,9 +594,27 @@ macro_rules! __itconfig_variable { } }; + ( + meta = [$(#$meta:tt,)*], + concat = [$($concat:expr,)+], + name = $name:ident, + env_prefix = $env_prefix:expr, + env_name = $env_name:expr, + ty = $ty:ty, + $($args:tt)* + ) => { + $(#$meta)* + pub fn $name() -> $ty { + let value_parts: Vec = vec!($($concat),+); + let value = value_parts.join(""); + env_or!(@default $env_name, value) + } + }; + // Add method ( meta = [$(#$meta:tt,)*], + concat = [], name = $name:ident, env_prefix = $env_prefix:expr, env_name = $env_name:expr, diff --git a/itconfig_tests/tests/config_macro.rs b/itconfig_tests/tests/config_macro.rs index 00c16df..2ea2b4e 100644 --- a/itconfig_tests/tests/config_macro.rs +++ b/itconfig_tests/tests/config_macro.rs @@ -5,7 +5,7 @@ extern crate itconfig; #[test] -#[should_panic] +#[should_panic(expected = "Cannot read \"MISS_VARIABLE\" environment variable")] fn should_panic_if_miss_env_variable() { config! { MISS_VARIABLE: bool, @@ -157,12 +157,12 @@ fn change_configuration_module_name() { #[test] fn configuration_with_namespace() { - env::set_var("POSTGRES_HOST", "t"); + env::set_var("DB_HOST", "t"); config! { DEBUG: bool => true, - POSTGRES { + DB { HOST: bool, PORT: bool => true, USERNAME: bool => true, @@ -173,8 +173,7 @@ fn configuration_with_namespace() { cfg::init(); assert_eq!(cfg::DEBUG(), true); - assert_eq!(cfg::POSTGRES::HOST(), true); - env::remove_var("POSTGRES_HOST"); + assert_eq!(cfg::DB::HOST(), true); } @@ -194,8 +193,6 @@ fn configuration_variables_and_namespace_in_lowercase() { cfg::init(); assert_eq!(cfg::testing(), true); assert_eq!(cfg::namespace::foo(), true); - env::remove_var("TESTING"); - env::remove_var("NAMESPACE_FOO"); } @@ -216,7 +213,6 @@ fn custom_environment_name_for_variable() { cfg::init(); assert_eq!(cfg::PER_PAGE(), 95); assert_eq!(cfg::APP::RECIPES_PER_PAGE(), 95); - env::remove_var("MY_CUSTOM_NAME"); } #[test] @@ -239,7 +235,6 @@ fn stranger_meta_data() { #[cfg(feature = "postgres")] assert_eq!(cfg::DATABASE_URL(), "95"); - env::remove_var("MY_CUSTOM_NAME"); } #[test] @@ -259,3 +254,64 @@ fn setting_default_env_variable() { assert_eq!(env::var("DEFAULT_ENV_FLOAT"), Ok("40.9".to_string())); } + +#[test] +fn concatenate_environment_variables() { + env::set_var("POSTGRES_USERNAME", "user"); + env::set_var("POSTGRES_PASSWORD", "pass"); + env::set_var("POSTGRES_HOST", "localhost"); + env::set_var("POSTGRES_DB", "test"); + + config! { + DATABASE_URL < ( + "postgres://", + POSTGRES_USERNAME, + ":", + POSTGRES_PASSWORD, + "@", + POSTGRES_HOST, + "/", + POSTGRES_DB, + ), + } + + cfg::init(); + assert_eq!(cfg::DATABASE_URL(), String::from("postgres://user:pass@localhost/test")); +} + + +#[test] +fn setting_default_concat_env_variable() { + env::set_var("SETTING_DEFAULT_CONCAT_ENV_VARIABLE", "custom"); + + config! { + DEFAULT_CONCAT_ENV < ( + "string", + "/", + SETTING_DEFAULT_CONCAT_ENV_VARIABLE, + ), + } + + cfg::init(); + assert_eq!(env::var("DEFAULT_CONCAT_ENV"), Ok("string/custom".to_string())); +} + + +#[test] +#[should_panic(expected = "Cannot read \"PG_USERNAME\" environment variable")] +fn concatenate_not_defined_environment_variables() { + config! { + DATABASE_URL < ( + "postgres://", + PG_USERNAME, + ":", + PG_PASSWORD, + "@", + PG_HOST, + "/", + PG_DB, + ), + } + cfg::init(); +} +