From 1a5154d261670652bcc7ba280caaba818c283531 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 10 Jan 2020 23:42:41 +0300 Subject: [PATCH 1/2] feat: add nested namespaces --- README.md | 40 ++++++--- itconfig/README.md | 40 ++++++--- itconfig/src/lib.rs | 123 +++++++++++++++++++-------- itconfig_tests/Cargo.toml | 4 + itconfig_tests/tests/config_macro.rs | 33 +++++++ 5 files changed, 178 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 50d2487..3f0306e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,9 @@ use std::env; //use dotenv::dotenv; config! { - DEBUG: bool => true, + DEBUG: bool => false, + + #[env_name = "APP_HOST"] HOST: String => "127.0.0.1", DATABASE_URL < ( @@ -39,27 +41,37 @@ config! { "/", POSTGRES_DB => "test", ), - - NAMESPACE { - #[env_name = "MY_CUSTOM_NAME"] - FOO: bool, + + APP { + ARTICLE { + PER_PAGE: u32 => 15, + } - BAR: i32 => 10, - - #[cfg(feature = "feature")] - #[env_name = "POSTGRES_CONNECTION_STRING"] - DATABASE_URL: String + #[cfg(feature = "companies")] + COMPANY { + #[env_name = "INSTITUTIONS_PER_PAGE"] + PER_PAGE: u32 => 15, + } + } + + FEATURE { + NEW_MENU: bool => false, + + COMPANY { + PROFILE: bool => false, + } } } fn main () { // dotenv().ok(); - env::set_var("MY_CUSTOM_NAME", "t"); + env::set_var("FEATURE_NEW_MENU", "t"); cfg::init(); assert_eq!(cfg::HOST(), String::from("127.0.0.1")); assert_eq!(cfg::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test")); - assert_eq!(cfg::NAMESPACE::FOO(), true); + assert_eq!(cfg::APP:ARTICLE:PER_PAGE(), 15); + assert_eq!(cfg::FEATURE::NEW_MENU(), true); } ``` @@ -77,10 +89,12 @@ cargo test * [x] Support feature config and other meta directives * [x] Add default value to env if env is not found * [x] Concat env variables to one variable -* [ ] Add nested namespaces +* [x] Add nested namespaces +* [x] Support meta for namespaces * [ ] Support array type * [ ] Support hashmap type * [ ] Support custom env type +* [ ] Common configuration for namespace variables ## License diff --git a/itconfig/README.md b/itconfig/README.md index 3e92abc..2b5f561 100644 --- a/itconfig/README.md +++ b/itconfig/README.md @@ -13,7 +13,9 @@ use std::env; //use dotenv::dotenv; config! { - DEBUG: bool => true, + DEBUG: bool => false, + + #[env_name = "APP_HOST"] HOST: String => "127.0.0.1", DATABASE_URL < ( @@ -26,27 +28,37 @@ config! { "/", POSTGRES_DB => "test", ), - - NAMESPACE { - #[env_name = "MY_CUSTOM_NAME"] - FOO: bool, + + APP { + ARTICLE { + PER_PAGE: u32 => 15, + } - BAR: i32 => 10, - - #[cfg(feature = "feature")] - #[env_name = "POSTGRES_CONNECTION_STRING"] - DATABASE_URL: String + #[cfg(feature = "companies")] + COMPANY { + #[env_name = "INSTITUTIONS_PER_PAGE"] + PER_PAGE: u32 => 15, + } + } + + FEATURE { + NEW_MENU: bool => false, + + COMPANY { + PROFILE: bool => false, + } } } fn main () { // dotenv().ok(); - env::set_var("MY_CUSTOM_NAME", "t"); + env::set_var("FEATURE_NEW_MENU", "t"); cfg::init(); assert_eq!(cfg::HOST(), String::from("127.0.0.1")); assert_eq!(cfg::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test")); - assert_eq!(cfg::NAMESPACE::FOO(), true); + assert_eq!(cfg::APP:ARTICLE:PER_PAGE(), 15); + assert_eq!(cfg::FEATURE::NEW_MENU(), true); } ``` @@ -58,10 +70,12 @@ fn main () { * [x] Support feature config and other meta directives * [x] Add default value to env if env is not found * [x] Concat env variables to one variable -* [ ] Add nested namespaces +* [x] Add nested namespaces +* [x] Support meta for namespaces * [ ] Support array type * [ ] Support hashmap type * [ ] Support custom env type +* [ ] Common configuration for namespace variables ## License diff --git a/itconfig/src/lib.rs b/itconfig/src/lib.rs index d100b80..c17ff14 100644 --- a/itconfig/src/lib.rs +++ b/itconfig/src/lib.rs @@ -25,22 +25,36 @@ //! POSTGRES_DB => "test", //! ), //! -//! NAMESPACE { -//! #[env_name = "MY_CUSTOM_NAME"] -//! FOO: bool, +//! APP { +//! ARTICLE { +//! PER_PAGE: u32 => 15, +//! } //! -//! BAR: i32 => 10, +//! #[cfg(feature = "companies")] +//! COMPANY { +//! #[env_name = "INSTITUTIONS_PER_PAGE"] +//! PER_PAGE: u32 => 15, +//! } +//! } +//! +//! FEATURE { +//! NEW_MENU: bool => false, +//! +//! COMPANY { +//! PROFILE: bool => false, +//! } //! } //! } //! //! fn main () { //! // dotenv().ok(); -//! env::set_var("MY_CUSTOM_NAME", "t"); +//! env::set_var("FEATURE_NEW_MENU", "t"); //! //! cfg::init(); //! assert_eq!(cfg::HOST(), String::from("127.0.0.1")); //! assert_eq!(cfg::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test")); -//! assert_eq!(cfg::NAMESPACE::FOO(), true); +//! assert_eq!(cfg::APP::ARTICLE::PER_PAGE(), 15); +//! assert_eq!(cfg::FEATURE::NEW_MENU(), true); //! } //! ``` @@ -165,6 +179,41 @@ impl From for String { /// # cfg::init() /// ``` /// +/// Now you can use nested structure in namespaces without limits :) +/// +/// ```rust +/// # #[macro_use] extern crate itconfig; +/// config! { +/// FIRST { +/// SECOND { +/// THIRD { +/// FOO: bool => true, +/// } +/// } +/// } +/// } +/// # cfg::init(); +/// ``` +/// +/// Namespaces supports custom meta +/// +/// ```rust +/// # #[macro_use] extern crate itconfig; +/// config! { +/// #[cfg(feature = "first")] +/// FIRST { +/// #[cfg(feature = "second")] +/// SECOND { +/// #[cfg(feature = "third")] +/// THIRD { +/// FOO: bool => true, +/// } +/// } +/// } +/// } +/// # cfg::init(); +/// ``` +/// /// Meta /// ---- /// @@ -343,6 +392,7 @@ macro_rules! __itconfig_parse_module { module = { env_prefix = "", name = $name, + meta = [], }, } }; @@ -360,6 +410,7 @@ macro_rules! __itconfig_parse_variables { // Find namespace ( tokens = [ + $(#$meta:tt)* $ns_name:ident { $($ns_tokens:tt)* } $($rest:tt)* ], @@ -368,9 +419,11 @@ macro_rules! __itconfig_parse_variables { __itconfig_parse_variables! { tokens = [$($ns_tokens)*], variables = [], + namespaces = [], module = { env_prefix = concat!(stringify!($ns_name), "_"), name = $ns_name, + meta = [$(#$meta)*], }, callback = { tokens = [$($rest)*], @@ -526,9 +579,8 @@ macro_rules! __itconfig_parse_variables { ( tokens = [], variables = $ns_variables:tt, - module = { - $($current_namespace:tt)* - }, + namespaces = $ns_namespaces:tt, + module = $ns_module:tt, callback = { tokens = $tokens:tt, variables = $variables:tt, @@ -543,7 +595,8 @@ macro_rules! __itconfig_parse_variables { $($namespaces,)* { variables = $ns_variables, - $($current_namespace)* + namespaces = $ns_namespaces, + module = $ns_module, }, ], $($args)* @@ -555,7 +608,7 @@ macro_rules! __itconfig_parse_variables { tokens = [], $($args:tt)* ) => { - __itconfig_impl!($($args)*); + __itconfig_impl_namespace!($($args)*); }; // Invalid syntax @@ -567,7 +620,7 @@ macro_rules! __itconfig_parse_variables { #[macro_export] #[doc(hidden)] -macro_rules! __itconfig_impl { +macro_rules! __itconfig_impl_namespace { ( variables = [$({ meta = $var_meta:tt, @@ -576,44 +629,42 @@ macro_rules! __itconfig_impl { $($variable:tt)* },)*], namespaces = [$({ - variables = [$({ - meta = $ns_var_meta:tt, - concat = $ns_var_concat:tt, - name = $ns_var_name:ident, - $($ns_variables:tt)* - },)*], - env_prefix = $ns_env_prefix:expr, - name = $ns_name:ident, + variables = $ns_variable:tt, + namespaces = $ns_namespaces:tt, + module = { + env_prefix = $ns_env_prefix:expr, + name = $ns_mod_name:ident, + meta = [$(#$ns_meta:tt)*], + }, },)*], module = { env_prefix = $env_prefix:expr, name = $mod_name:ident, + meta = [$(#$meta:tt)*], }, ) => { + $(#$meta)* 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)* - })* - } - )* + $(__itconfig_impl_namespace! { + variables = $ns_variable, + namespaces = $ns_namespaces, + module = { + env_prefix = $ns_env_prefix, + name = $ns_mod_name, + meta = [$(#$ns_meta)*], + }, + })* pub fn init() { $($var_name();)* - - $($($ns_name::$ns_var_name();)*)* + $( + $(#$ns_meta)* + $ns_mod_name::init(); + )* } $(__itconfig_variable! { diff --git a/itconfig_tests/Cargo.toml b/itconfig_tests/Cargo.toml index 6cb956b..00ba8e3 100644 --- a/itconfig_tests/Cargo.toml +++ b/itconfig_tests/Cargo.toml @@ -10,3 +10,7 @@ publish = false [dependencies] itconfig = { path = '../itconfig' } + +[features] +default = ["meta_namespace"] +meta_namespace = [] diff --git a/itconfig_tests/tests/config_macro.rs b/itconfig_tests/tests/config_macro.rs index 7373439..3f8685d 100644 --- a/itconfig_tests/tests/config_macro.rs +++ b/itconfig_tests/tests/config_macro.rs @@ -179,6 +179,39 @@ fn configuration_with_namespace() { assert_eq!(cfg::DB::HOST(), true); } +#[test] +fn configuration_with_nested_namespaces() { + config! { + FIRST { + SECOND { + THIRD { + FOO: u32 => 50, + } + } + } + } + + cfg::init(); + assert_eq!(cfg::FIRST::SECOND::THIRD::FOO(), 50); +} + +#[cfg(feature = "meta_namespace")] +#[test] +fn configuration_namespaces_with_custom_meta() { + config! { + FIRST { + #[cfg(feature = "meta_namespace")] + SECOND { + THIRD { + FOO: u32 => 50, + } + } + } + } + + cfg::init(); + assert_eq!(cfg::FIRST::SECOND::THIRD::FOO(), 50); +} #[test] fn configuration_variables_and_namespace_in_lowercase() { From 8e56cc75efc60de5302cadeff669e5586d69a5a7 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 10 Jan 2020 23:46:49 +0300 Subject: [PATCH 2/2] chore: update version --- itconfig/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itconfig/Cargo.toml b/itconfig/Cargo.toml index 7956b71..9c424ab 100644 --- a/itconfig/Cargo.toml +++ b/itconfig/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "itconfig" -version = "0.7.1" +version = "0.8.0" authors = ["Dmitriy Pleshevskiy "] description = "Easy build a configs from environment variables and use it in globally." categories = ["config", "web-programming"]