Merge pull request #18 from icetemple/task-16
feat: add itconfig proc macro
This commit is contained in:
commit
e501831014
20 changed files with 1258 additions and 1144 deletions
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"itconfig",
|
||||
"itconfig-macro",
|
||||
"itconfig-tests",
|
||||
"examples/diesel",
|
||||
"examples/rocket", # nightly
|
||||
|
@ -9,5 +10,6 @@ members = [
|
|||
|
||||
default-members = [
|
||||
"itconfig",
|
||||
"itconfig-macro",
|
||||
"itconfig-tests",
|
||||
]
|
||||
|
|
33
README.md
33
README.md
|
@ -18,10 +18,29 @@ where you need variable. It uses little bit memory, but configuration lifetime i
|
|||
as application lifetime. Because of it I decided to create my own library.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
These macros require a Rust compiler version 1.31 or newer.
|
||||
|
||||
Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`.
|
||||
|
||||
`Cargo.toml` example:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "my-crate"
|
||||
version = "0.1.0"
|
||||
authors = ["Me <user@rust-lang.org>"]
|
||||
|
||||
[dependencies]
|
||||
itconfig = { version = "1.0", features = ["macro"] }
|
||||
```
|
||||
|
||||
|
||||
## Example usage
|
||||
|
||||
```rust
|
||||
#[macro_use] extern crate itconfig;
|
||||
use itconfig::config;
|
||||
use std::env;
|
||||
//use dotenv::dotenv;
|
||||
|
||||
|
@ -69,11 +88,11 @@ fn main () {
|
|||
// dotenv().ok();
|
||||
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::APP:ARTICLE:PER_PAGE(), 15);
|
||||
assert_eq!(cfg::FEATURE::NEW_MENU(), true);
|
||||
config::init();
|
||||
assert_eq!(config::HOST(), String::from("127.0.0.1"));
|
||||
assert_eq!(config::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test"));
|
||||
assert_eq!(config::APP:ARTICLE:PER_PAGE(), 15);
|
||||
assert_eq!(config::FEATURE::NEW_MENU(), true);
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -112,6 +131,7 @@ cargo test
|
|||
* [x] Add nested namespaces
|
||||
* [x] Support meta for namespaces
|
||||
* [x] Support array type
|
||||
* [x] Rewrite to proc macro
|
||||
* [ ] Support hashmap type
|
||||
* [ ] Support custom env type
|
||||
* [ ] Common configuration for namespace variables
|
||||
|
@ -121,7 +141,6 @@ cargo test
|
|||
|
||||
* **default** - ["macro", "primitives", "static"]
|
||||
* **macro** - Activates `config!` macros for easy configure web application.
|
||||
* **static** - Add `static` option to `config!` macros (uses optional `lazy_static` package).
|
||||
* **array** - Add EnvString impl for vector type (uses optional `serde_json` package).
|
||||
* **primitives** - Group for features: `numbers` and `bool`.
|
||||
* **numbers** - Group for features: `int`, `uint` and `float`.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use super::cfg;
|
||||
use super::config;
|
||||
use diesel::prelude::*;
|
||||
use diesel::pg::PgConnection;
|
||||
|
||||
pub fn establish_connection() -> PgConnection {
|
||||
let database_url = cfg::DATABASE_URL();
|
||||
let database_url = config::DATABASE_URL();
|
||||
PgConnection::establish(database_url)
|
||||
.expect(&format!("Error connecting to {}", database_url))
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#[macro_use]
|
||||
extern crate itconfig;
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
mod db;
|
||||
mod models;
|
||||
mod schema;
|
||||
|
||||
use itconfig::config;
|
||||
use dotenv::dotenv;
|
||||
use diesel::prelude::*;
|
||||
use crate::models::*;
|
||||
|
@ -19,7 +18,7 @@ config! {
|
|||
|
||||
fn main() {
|
||||
dotenv().ok();
|
||||
cfg::init();
|
||||
config::init();
|
||||
|
||||
let connection = db::establish_connection();
|
||||
let posts = get_posts(&connection);
|
||||
|
|
|
@ -14,6 +14,7 @@ tokio = { version = "0.2", features = ["macros"] }
|
|||
bytes = "0.5"
|
||||
futures-util = { version = "0.3", default-features = false }
|
||||
pretty_env_logger = "0.3"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = ["static"]
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#[macro_use]
|
||||
extern crate itconfig;
|
||||
|
||||
use itconfig::config;
|
||||
use bytes::buf::BufExt;
|
||||
use futures_util::{stream, StreamExt};
|
||||
use hyper::client::HttpConnector;
|
||||
|
@ -41,7 +39,7 @@ const POST_DATA: &'static str = r#"{"original": "data"}"#;
|
|||
async fn client_request_response(client: &Client<HttpConnector>) -> HyperResult<Response<Body>> {
|
||||
let req = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(cfg::HYPER::JSON_API_URL())
|
||||
.uri(config::HYPER::JSON_API_URL())
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(POST_DATA))
|
||||
.unwrap();
|
||||
|
@ -113,10 +111,10 @@ async fn response_examples(
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> HyperResult<()> {
|
||||
cfg::init();
|
||||
config::init();
|
||||
pretty_env_logger::init();
|
||||
|
||||
let addr = cfg::HYPER::HOST().parse().unwrap();
|
||||
let addr = config::HYPER::HOST().parse().unwrap();
|
||||
|
||||
// Share a `Client` with all `Service`s
|
||||
let client = Client::new();
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
#[macro_use]
|
||||
extern crate itconfig;
|
||||
|
||||
use itconfig::config;
|
||||
|
||||
config! {
|
||||
ROCKET {
|
||||
|
@ -21,9 +20,9 @@ fn index() -> &'static str {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
cfg::init();
|
||||
config::init();
|
||||
|
||||
rocket::ignite()
|
||||
.mount(cfg::ROCKET::BASE_URL(), routes![index])
|
||||
.mount(config::ROCKET::BASE_URL(), routes![index])
|
||||
.launch();
|
||||
}
|
30
itconfig-macro/Cargo.toml
Normal file
30
itconfig-macro/Cargo.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "itconfig-macro"
|
||||
version = "1.0.0"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
description = "Easy build a configs from environment variables and use it in globally."
|
||||
categories = ["config", "web-programming"]
|
||||
keywords = ["config", "env", "configuration", "environment", "macro"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/icetemple/itconfig-rs"
|
||||
homepage = "https://github.com/icetemple/itconfig-rs"
|
||||
documentation = "https://docs.rs/itconfig"
|
||||
readme = "../itconfig/README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0.16"
|
||||
quote = "1.0.3"
|
||||
proc-macro2 = "1.0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
itconfig = { path = "../itconfig" }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "icetemple/itconfig-rs" }
|
||||
maintenance = { status = "actively-developed" }
|
30
itconfig-macro/src/ast.rs
Normal file
30
itconfig-macro/src/ast.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn::{Type, Expr, Ident, Attribute};
|
||||
|
||||
|
||||
pub struct RootNamespace {
|
||||
pub name: Option<Ident>,
|
||||
pub variables: Vec<Variable>,
|
||||
pub namespaces: Vec<Namespace>,
|
||||
pub meta: Vec<Attribute>,
|
||||
}
|
||||
|
||||
|
||||
pub struct Namespace {
|
||||
pub name: Ident,
|
||||
pub variables: Vec<Variable>,
|
||||
pub namespaces: Vec<Namespace>,
|
||||
pub env_prefix: Option<String>,
|
||||
pub meta: Vec<Attribute>,
|
||||
}
|
||||
|
||||
|
||||
pub struct Variable {
|
||||
pub is_static: bool,
|
||||
pub name: Ident,
|
||||
pub ty: Type,
|
||||
pub initial: Option<Expr>,
|
||||
pub concat_parts: Option<Vec<TokenStream2>>,
|
||||
pub env_name: Option<String>,
|
||||
pub meta: Vec<Attribute>,
|
||||
}
|
170
itconfig-macro/src/expand.rs
Normal file
170
itconfig-macro/src/expand.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
use crate::ast::*;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
|
||||
fn vec_to_token_stream_2<T>(input: &Vec<T>) -> Vec<TokenStream2>
|
||||
where T: ToTokens
|
||||
{
|
||||
input.iter()
|
||||
.map(|ns| ns.into_token_stream())
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
impl ToTokens for RootNamespace {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = &self.name;
|
||||
let variables = vec_to_token_stream_2(&self.variables);
|
||||
let namespaces = vec_to_token_stream_2(&self.namespaces);
|
||||
|
||||
let init_variables = self.variables.iter()
|
||||
.map(|var| {
|
||||
let name = &var.name;
|
||||
let var_meta = vec_to_token_stream_2(&var.meta);
|
||||
|
||||
quote!(
|
||||
#(#var_meta)*
|
||||
#name();
|
||||
)
|
||||
})
|
||||
.collect::<Vec<TokenStream2>>();
|
||||
let init_namespaces = self.namespaces.iter()
|
||||
.map(|ns| {
|
||||
let name = &ns.name;
|
||||
let ns_meta = vec_to_token_stream_2(&ns.meta);
|
||||
|
||||
quote!(
|
||||
#(#ns_meta)*
|
||||
#name::init();
|
||||
)
|
||||
})
|
||||
.collect::<Vec<TokenStream2>>();
|
||||
|
||||
let inner_meta: Vec<TokenStream2> = if name.is_none() {
|
||||
vec![]
|
||||
} else if self.meta.is_empty() {
|
||||
vec![quote!(#![allow(non_snake_case)])]
|
||||
} else {
|
||||
vec_to_token_stream_2(&self.meta)
|
||||
};
|
||||
|
||||
|
||||
let inner_rules = quote! {
|
||||
#(#inner_meta)*
|
||||
|
||||
#(#namespaces)*
|
||||
|
||||
#(#variables)*
|
||||
|
||||
pub fn init() {
|
||||
#(#init_variables)*
|
||||
#(#init_namespaces)*
|
||||
}
|
||||
};
|
||||
|
||||
tokens.append_all(
|
||||
match self.name.as_ref() {
|
||||
None => inner_rules,
|
||||
Some(name) => quote! {
|
||||
pub mod #name {
|
||||
#inner_rules
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToTokens for Namespace {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = &self.name;
|
||||
let variables = vec_to_token_stream_2(&self.variables);
|
||||
let namespaces = vec_to_token_stream_2(&self.namespaces);
|
||||
let meta = vec_to_token_stream_2(&self.meta);
|
||||
|
||||
let init_variables = self.variables.iter()
|
||||
.map(|var| {
|
||||
let name = &var.name;
|
||||
let var_meta = vec_to_token_stream_2(&var.meta);
|
||||
|
||||
quote!(
|
||||
#(#var_meta)*
|
||||
#name();
|
||||
)
|
||||
})
|
||||
.collect::<Vec<TokenStream2>>();
|
||||
let init_namespaces = self.namespaces.iter()
|
||||
.map(|ns| {
|
||||
let name = &ns.name;
|
||||
let ns_meta = vec_to_token_stream_2(&ns.meta);
|
||||
|
||||
quote!(
|
||||
#(#ns_meta)*
|
||||
#name::init();
|
||||
)
|
||||
})
|
||||
.collect::<Vec<TokenStream2>>();
|
||||
|
||||
tokens.append_all(quote!(
|
||||
#(#meta)*
|
||||
pub mod #name {
|
||||
#(#namespaces)*
|
||||
|
||||
#(#variables)*
|
||||
|
||||
pub fn init() {
|
||||
#(#init_variables)*
|
||||
#(#init_namespaces)*
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToTokens for Variable {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let ty = &self.ty;
|
||||
let name = &self.name;
|
||||
let env_name = &self.env_name.clone()
|
||||
.unwrap_or(name.to_string().to_uppercase());
|
||||
let meta = vec_to_token_stream_2(&self.meta);
|
||||
|
||||
let get_variable: TokenStream2 = if self.concat_parts.is_some() {
|
||||
let concat_parts = self.concat_parts.as_ref().unwrap();
|
||||
quote! {{
|
||||
let value_parts: Vec<String> = vec!(#(#concat_parts),*);
|
||||
let value = value_parts.join("");
|
||||
::std::env::set_var(#env_name, value.as_str());
|
||||
value
|
||||
}}
|
||||
} else if self.initial.is_some() {
|
||||
let initial = self.initial.as_ref().unwrap();
|
||||
quote!(::itconfig::get_env_or_set_default(#env_name, #initial))
|
||||
} else {
|
||||
quote!(::itconfig::get_env_or_panic(#env_name))
|
||||
};
|
||||
|
||||
if self.is_static {
|
||||
tokens.append_all(quote!(
|
||||
#(#meta)*
|
||||
pub fn #name() -> #ty {
|
||||
::lazy_static::lazy_static! {
|
||||
static ref #name: #ty = #get_variable;
|
||||
}
|
||||
|
||||
(*#name).clone()
|
||||
}
|
||||
));
|
||||
} else {
|
||||
tokens.append_all(quote!(
|
||||
#(#meta)*
|
||||
pub fn #name() -> #ty {
|
||||
#get_variable
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
338
itconfig-macro/src/lib.rs
Normal file
338
itconfig-macro/src/lib.rs
Normal file
|
@ -0,0 +1,338 @@
|
|||
#![recursion_limit = "256"]
|
||||
|
||||
mod ast;
|
||||
mod parse;
|
||||
mod expand;
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
use self::proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::parse_macro_input;
|
||||
use ast::RootNamespace;
|
||||
|
||||
|
||||
/// ### _This API requires the following crate features to be activated: `macro`_
|
||||
///
|
||||
/// Creates new public mod with function fo get each environment variable of mapping.
|
||||
///
|
||||
/// All variables are required and program will panic if some variables haven't value, but you
|
||||
/// can add default value for specific variable.
|
||||
///
|
||||
/// Starts with v0.6.0 if you don't have an optional variable, the variable is set automatically.
|
||||
///
|
||||
/// Example usage
|
||||
/// -------------
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
/// config! {
|
||||
/// DATABASE_URL: String,
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {
|
||||
/// # env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
|
||||
/// # config::init();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Config with default value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
/// config! {
|
||||
/// DATABASE_URL: String,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
/// # fn main() {
|
||||
/// # env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
|
||||
/// # config::init();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// By default itconfig lib creates module with 'config' name. But you can use simple meta instruction
|
||||
/// if you want to rename module. In the example below we renamed module to 'configuration'
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
/// config! {
|
||||
/// #![config(name = "configuration")]
|
||||
///
|
||||
/// DEBUG: bool,
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// env::set_var("DEBUG", "t");
|
||||
///
|
||||
/// configuration::init();
|
||||
/// assert_eq!(configuration::DEBUG(), true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You also unwrap first config module
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
///
|
||||
/// config! {
|
||||
/// #![config(unwrap)]
|
||||
///
|
||||
/// DEBUG: bool,
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// env::set_var("DEBUG", "t");
|
||||
///
|
||||
/// init();
|
||||
/// assert_eq!(DEBUG(), true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// Namespaces
|
||||
/// ----------
|
||||
///
|
||||
/// You can use namespaces for env variables
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
///
|
||||
/// config! {
|
||||
/// DEBUG: bool,
|
||||
/// DATABASE {
|
||||
/// USERNAME: String,
|
||||
/// PASSWORD: String,
|
||||
/// HOST: String,
|
||||
/// }
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// env::set_var("DEBUG", "t");
|
||||
/// env::set_var("DATABASE_USERNAME", "user");
|
||||
/// env::set_var("DATABASE_PASSWORD", "pass");
|
||||
/// env::set_var("DATABASE_HOST", "localhost");
|
||||
///
|
||||
/// config::init();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Now you can use nested structure in namespaces without limits :)
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// config! {
|
||||
/// FIRST {
|
||||
/// SECOND {
|
||||
/// THIRD {
|
||||
/// FOO: bool => true,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # fn main() { config::init () }
|
||||
/// ```
|
||||
///
|
||||
/// Namespaces supports custom meta
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// config! {
|
||||
/// #[cfg(feature = "first")]
|
||||
/// FIRST {
|
||||
/// #[cfg(feature = "second")]
|
||||
/// SECOND {
|
||||
/// #[cfg(feature = "third")]
|
||||
/// THIRD {
|
||||
/// FOO: bool => true,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # fn main() { config::init () }
|
||||
/// ```
|
||||
///
|
||||
/// Meta
|
||||
/// ----
|
||||
///
|
||||
/// If you want to read custom env name for variable you can change it manually.
|
||||
///
|
||||
/// **A variable in the nameespace will lose environment prefix**
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
///
|
||||
/// config! {
|
||||
/// #[env_name = "MY_CUSTOM_NAME"]
|
||||
/// PER_PAGE: i32,
|
||||
///
|
||||
/// APP {
|
||||
/// #[env_name = "MY_CUSTOM_NAME"]
|
||||
/// RECIPES_PER_PAGE: i32,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// env::set_var("MY_CUSTOM_NAME", "95");
|
||||
///
|
||||
/// config::init();
|
||||
/// assert_eq!(config::PER_PAGE(), 95);
|
||||
/// assert_eq!(config::APP::RECIPES_PER_PAGE(), 95);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Also you can add custom meta for each variable. For example feature configurations.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// config! {
|
||||
/// #[cfg(feature = "postgres")]
|
||||
/// DATABASE_URL: String,
|
||||
///
|
||||
/// #[cfg(not(feature = "postgres"))]
|
||||
/// DATABASE_URL: String,
|
||||
/// }
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Concatenate
|
||||
/// -----------
|
||||
///
|
||||
/// Try to concatenate env variable or strings or both to you env variable. It's easy!
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
/// config! {
|
||||
/// DATABASE_URL < (
|
||||
/// "postgres://",
|
||||
/// POSTGRES_USERNAME,
|
||||
/// ":",
|
||||
/// POSTGRES_PASSWORD,
|
||||
/// "@",
|
||||
/// POSTGRES_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// POSTGRES_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// env::set_var("POSTGRES_USERNAME", "user");
|
||||
/// env::set_var("POSTGRES_PASSWORD", "pass");
|
||||
///
|
||||
/// config::init();
|
||||
/// assert_eq!(config::DATABASE_URL(), "postgres://user:pass@localhost:5432/test".to_string());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Concatinated variables can be only strings and support all features like namespaces and meta.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// config! {
|
||||
/// CONCATED_NAMESPACE {
|
||||
/// #[env_name = "DATABASE_URL"]
|
||||
/// CONCAT_ENVVAR < (
|
||||
/// "postgres://",
|
||||
/// NOT_DEFINED_PG_USERNAME => "user",
|
||||
/// ":",
|
||||
/// NOT_DEFINED_PG_PASSWORD => "pass",
|
||||
/// "@",
|
||||
/// NOT_DEFINED_PG_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// NOT_DEFINED_PG_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// # fn main() { config::init () }
|
||||
/// ```
|
||||
///
|
||||
/// Static
|
||||
/// ------
|
||||
///
|
||||
/// Starting with 0.11 version you can use lazy static for improve speed of variable. This is very
|
||||
/// useful, if you use variable more than once.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// # use std::env;
|
||||
/// config! {
|
||||
/// static APP_BASE_URL => "/api",
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// env::set_var("APP_BASE_URL", "/api/v1");
|
||||
///
|
||||
/// config::init();
|
||||
/// assert_eq!(config::APP_BASE_URL(), "/api/v1");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You also can use static with concat variables
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// config! {
|
||||
/// static CONNECTION_STRING < (
|
||||
/// "postgres://",
|
||||
/// NOT_DEFINED_PG_USERNAME => "user",
|
||||
/// ":",
|
||||
/// NOT_DEFINED_PG_PASSWORD => "pass",
|
||||
/// "@",
|
||||
/// NOT_DEFINED_PG_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// NOT_DEFINED_PG_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// config::init();
|
||||
/// assert_eq!(config::CONNECTION_STRING(), "postgres://user:pass@localhost:5432/test".to_string());
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// This module will also contain helper method:
|
||||
/// --------------------------------------------
|
||||
///
|
||||
/// ```rust
|
||||
/// pub fn init() {}
|
||||
/// ```
|
||||
///
|
||||
/// Run this at the main function for check all required variables without default value.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
///
|
||||
/// If you miss some required variables your application will panic at startup.
|
||||
///
|
||||
/// Examples
|
||||
/// --------
|
||||
///
|
||||
/// ```rust
|
||||
/// # use itconfig::config;
|
||||
/// // use dotenv::dotenv;
|
||||
///
|
||||
/// config! {
|
||||
/// DEBUG: bool => true,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// // dotenv().ok();
|
||||
/// config::init();
|
||||
/// assert_eq!(config::HOST(), String::from("127.0.0.1"));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[proc_macro]
|
||||
pub fn config(input: TokenStream) -> TokenStream {
|
||||
let namespace = parse_macro_input!(input as RootNamespace);
|
||||
namespace.into_token_stream().into()
|
||||
}
|
262
itconfig-macro/src/parse.rs
Normal file
262
itconfig-macro/src/parse.rs
Normal file
|
@ -0,0 +1,262 @@
|
|||
use crate::ast::*;
|
||||
use syn::parse::{Parse, ParseStream, Result, ParseBuffer};
|
||||
use syn::token::{FatArrow, Comma, Colon, Brace, Lt};
|
||||
use syn::{braced, parenthesized, Type, Expr, Token, Lit, Attribute, Meta, MetaNameValue, Error, parse_str, MetaList, NestedMeta};
|
||||
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
|
||||
use syn::ext::IdentExt;
|
||||
use quote::quote;
|
||||
|
||||
fn fill_env_prefix(
|
||||
prefix: String
|
||||
) -> Box<dyn Fn(Namespace) -> Namespace> {
|
||||
Box::new(move |mut ns| {
|
||||
let env_prefix = match &ns.env_prefix {
|
||||
None => {
|
||||
let env_prefix = format!(
|
||||
"{}{}_",
|
||||
prefix,
|
||||
ns.name.clone().to_string()
|
||||
);
|
||||
ns.env_prefix = Some(env_prefix.clone());
|
||||
env_prefix
|
||||
}
|
||||
Some(env_prefix) => {
|
||||
env_prefix.clone()
|
||||
}
|
||||
};
|
||||
|
||||
if !ns.namespaces.is_empty() {
|
||||
ns.namespaces = ns.namespaces.into_iter()
|
||||
.map(fill_env_prefix(ns.env_prefix.clone().unwrap()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
if !ns.variables.is_empty() {
|
||||
ns.variables = ns.variables.into_iter()
|
||||
.map(|mut var| {
|
||||
if var.env_name.is_none() {
|
||||
var.env_name = Some(
|
||||
format!("{}{}", env_prefix.clone(), &var.name.to_string())
|
||||
.to_uppercase()
|
||||
);
|
||||
}
|
||||
var
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
ns
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_namespace_content(
|
||||
input: &ParseBuffer,
|
||||
variables: &mut Vec<Variable>,
|
||||
namespaces: &mut Vec<Namespace>,
|
||||
) -> Result<()> {
|
||||
let attributes: Vec<Attribute> = input.call(Attribute::parse_outer)?;
|
||||
if input.peek2(Brace) {
|
||||
let mut namespace: Namespace = input.parse()?;
|
||||
|
||||
for attr in attributes {
|
||||
if attr.path.is_ident("env_prefix") {
|
||||
namespace.env_prefix = parse_attribute(attr, "env_prefix", &namespace.env_prefix)?;
|
||||
} else {
|
||||
namespace.meta.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
namespaces.push(namespace);
|
||||
} else {
|
||||
let mut variable: Variable = input.parse()?;
|
||||
|
||||
for attr in attributes {
|
||||
if attr.path.is_ident("env_name") {
|
||||
variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?;
|
||||
} else {
|
||||
variable.meta.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
variables.push(variable);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn parse_attribute(attr: Attribute, name: &'static str, var: &Option<String>) -> Result<Option<String>> {
|
||||
if var.is_some() {
|
||||
let message = format!("You cannot use {} meta twice", &name);
|
||||
return Err(Error::new_spanned(attr, message));
|
||||
}
|
||||
|
||||
match attr.parse_meta()? {
|
||||
Meta::NameValue(MetaNameValue { lit: Lit::Str(lit_str), .. }) => {
|
||||
Ok(Some(lit_str.value()))
|
||||
}
|
||||
_ => {
|
||||
let message = format!("expected #[{} = \"...\"]", &name);
|
||||
Err(Error::new_spanned(attr, message))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Parse for RootNamespace {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut name: Option<Ident> = None;
|
||||
let mut with_module = true;
|
||||
let mut meta: Vec<Attribute> = vec![];
|
||||
|
||||
let attributes: Vec<Attribute> = input.call(Attribute::parse_inner)?;
|
||||
for attr in attributes {
|
||||
if attr.path.is_ident("config") {
|
||||
match attr.parse_meta()? {
|
||||
Meta::List(MetaList { nested, .. }) => {
|
||||
let message = format!("expected #[config(name = \"...\")] or #[config(unwrap)]");
|
||||
match nested.first().unwrap() {
|
||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit: Lit::Str(lit_str), .. })) => {
|
||||
if path.is_ident("name") {
|
||||
name = Some(Ident::new(&lit_str.value(), Span::call_site()));
|
||||
} else {
|
||||
Err(Error::new_spanned(attr, message))?;
|
||||
}
|
||||
},
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
if path.is_ident("unwrap") {
|
||||
name = None;
|
||||
with_module = false;
|
||||
} else {
|
||||
Err(Error::new_spanned(attr, message))?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Err(Error::new_spanned(attr, message))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let message = format!("expected #[config(...)]");
|
||||
Err(Error::new_spanned(attr, message))?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
meta.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
if with_module && name.is_none() {
|
||||
name = Some(Ident::new("config", Span::call_site()));
|
||||
}
|
||||
|
||||
let mut variables: Vec<Variable> = vec![];
|
||||
let mut namespaces: Vec<Namespace> = vec![];
|
||||
while !input.is_empty() {
|
||||
parse_namespace_content(&input, &mut variables, &mut namespaces)?;
|
||||
}
|
||||
|
||||
let prefix = String::new();
|
||||
let namespaces = namespaces.into_iter()
|
||||
.map(fill_env_prefix(prefix.clone()))
|
||||
.collect();
|
||||
|
||||
Ok(RootNamespace {
|
||||
name,
|
||||
variables,
|
||||
namespaces,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Parse for Namespace {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name: Ident = input.parse()?;
|
||||
let mut variables: Vec<Variable> = vec![];
|
||||
let mut namespaces: Vec<Namespace> = vec![];
|
||||
|
||||
let content;
|
||||
braced!(content in input);
|
||||
while !content.is_empty() {
|
||||
parse_namespace_content(&content, &mut variables, &mut namespaces)?;
|
||||
}
|
||||
|
||||
input.parse::<Comma>().ok();
|
||||
|
||||
Ok(Namespace {
|
||||
name,
|
||||
variables,
|
||||
namespaces,
|
||||
env_prefix: None,
|
||||
meta: vec![],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Parse for Variable {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let is_static = input.parse::<Token![static]>().ok().is_some();
|
||||
let name: Ident = input.parse()?;
|
||||
|
||||
let is_concat = input.peek(Lt);
|
||||
let mut concat_parts = None;
|
||||
let mut initial = None;
|
||||
|
||||
let ty: Type = if is_concat {
|
||||
parse_str("String")?
|
||||
} else if input.peek(Colon) {
|
||||
input.parse::<Colon>()?;
|
||||
input.parse()?
|
||||
} else {
|
||||
parse_str("&'static str")?
|
||||
};
|
||||
|
||||
if is_concat {
|
||||
input.parse::<Lt>()?;
|
||||
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
|
||||
let mut tmp_vec: Vec<TokenStream2> = vec![];
|
||||
while !content.is_empty() {
|
||||
if content.peek(Ident::peek_any) {
|
||||
let concat_var: Variable = content.parse()?;
|
||||
let name = &concat_var.name;
|
||||
let env_name = &concat_var.env_name.clone().unwrap_or(name.to_string());
|
||||
|
||||
let get_variable = if concat_var.initial.is_some() {
|
||||
let initial = concat_var.initial.as_ref().unwrap();
|
||||
quote!(::itconfig::get_env_or_set_default(#env_name, #initial))
|
||||
} else {
|
||||
quote!(::itconfig::get_env_or_panic(#env_name))
|
||||
};
|
||||
|
||||
tmp_vec.push(get_variable);
|
||||
} else {
|
||||
let part: Lit = content.parse()?;
|
||||
tmp_vec.push(quote!(#part.to_string()));
|
||||
}
|
||||
content.parse::<Comma>().ok();
|
||||
}
|
||||
concat_parts = Some(tmp_vec);
|
||||
} else {
|
||||
initial = input.parse::<FatArrow>().ok()
|
||||
.and_then(|_| input.parse::<Expr>().ok());
|
||||
};
|
||||
|
||||
input.parse::<Comma>().ok();
|
||||
|
||||
Ok(Variable {
|
||||
is_static,
|
||||
name,
|
||||
ty,
|
||||
initial,
|
||||
concat_parts,
|
||||
env_name: None,
|
||||
meta: vec![],
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ publish = false
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
itconfig = { path = '../itconfig' }
|
||||
itconfig = { path = '../itconfig', features = ["macro"] }
|
||||
criterion = "0.3.1"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
|
|
|
@ -59,22 +59,22 @@ fn source_macro_vs_lazy_macro(c: &mut Criterion) {
|
|||
|
||||
let source = Fun::new("source", |b, _| {
|
||||
b.iter(move || {
|
||||
assert_eq!(cfg::TEST(), "test");
|
||||
assert_eq!(config::TEST(), "test");
|
||||
})
|
||||
});
|
||||
let lazy = Fun::new("lazy", |b, _| {
|
||||
b.iter(move || {
|
||||
assert_eq!(cfg::LAZY_TEST(), "test");
|
||||
assert_eq!(config::LAZY_TEST(), "test");
|
||||
})
|
||||
});
|
||||
let source_with_default = Fun::new("source_with_default", |b, _| {
|
||||
b.iter(move || {
|
||||
assert_eq!(cfg::TEST_WITH_DEFAULT(), "default");
|
||||
assert_eq!(config::TEST_WITH_DEFAULT(), "default");
|
||||
})
|
||||
});
|
||||
let lazy_with_default = Fun::new("lazy_with_default", |b, _| {
|
||||
b.iter(move || {
|
||||
assert_eq!(cfg::LAZY_TEST_WITH_DEFAULT(), "default");
|
||||
assert_eq!(config::LAZY_TEST_WITH_DEFAULT(), "default");
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -1,74 +1,92 @@
|
|||
use std::env;
|
||||
use std::env::VarError;
|
||||
|
||||
#[macro_use]
|
||||
extern crate itconfig;
|
||||
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"MISS_VARIABLE\" is missing")]
|
||||
fn should_panic_if_miss_env_variable() {
|
||||
config! {
|
||||
mod test_case_1 {
|
||||
itconfig::config! {
|
||||
MISS_VARIABLE: bool,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"MISS_VARIABLE\" is missing")]
|
||||
fn should_panic_if_miss_env_variable() {
|
||||
config::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn one_variable() {
|
||||
env::set_var("DEBUG", "t");
|
||||
mod test_case_2 {
|
||||
use std::env;
|
||||
|
||||
config! {
|
||||
itconfig::config! {
|
||||
DEBUG: bool,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::DEBUG(), true);
|
||||
#[test]
|
||||
fn one_variable() {
|
||||
env::set_var("DEBUG", "t");
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::DEBUG(), true);
|
||||
env::remove_var("DEBUG");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_variable_with_default_value() {
|
||||
config! {
|
||||
|
||||
mod test_case_3 {
|
||||
itconfig::config! {
|
||||
DEBUG: bool => true,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::DEBUG(), true);
|
||||
#[test]
|
||||
fn one_variable_with_default_value() {
|
||||
config::init();
|
||||
assert_eq!(config::DEBUG(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn few_variables_with_default_value() {
|
||||
config! {
|
||||
mod test_case_4 {
|
||||
itconfig::config! {
|
||||
FOO: bool => true,
|
||||
BAR: bool => false,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::FOO(), true);
|
||||
assert_eq!(cfg::BAR(), false);
|
||||
#[test]
|
||||
fn few_variables_with_default_value() {
|
||||
config::init();
|
||||
assert_eq!(config::FOO(), true);
|
||||
assert_eq!(config::BAR(), false);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_types_with_default_value() {
|
||||
config! {
|
||||
mod test_case_5 {
|
||||
itconfig::config! {
|
||||
NUMBER: i32 => 30,
|
||||
BOOL: bool => true,
|
||||
STR: String => "str",
|
||||
STRING: String => "string".to_string(),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::NUMBER(), 30);
|
||||
assert_eq!(cfg::BOOL(), true);
|
||||
assert_eq!(cfg::STR(), "str".to_string());
|
||||
assert_eq!(cfg::STRING(), "string".to_string());
|
||||
#[test]
|
||||
fn different_types_with_default_value() {
|
||||
config::init();
|
||||
assert_eq!(config::NUMBER(), 30);
|
||||
assert_eq!(config::BOOL(), true);
|
||||
assert_eq!(config::STR(), "str".to_string());
|
||||
assert_eq!(config::STRING(), "string".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_bool_type_value_from_env() {
|
||||
mod test_case_6 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
T_BOOL: bool,
|
||||
TRUE_BOOL: bool,
|
||||
NUM_BOOL: bool,
|
||||
ON_BOOL: bool,
|
||||
CAMEL_CASE: bool,
|
||||
FALSE_BOOL: bool,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_bool_type_value_from_env() {
|
||||
env::set_var("T_BOOL", "t");
|
||||
env::set_var("TRUE_BOOL", "true");
|
||||
env::set_var("NUM_BOOL", "1");
|
||||
|
@ -76,43 +94,21 @@ fn convert_bool_type_value_from_env() {
|
|||
env::set_var("CAMEL_CASE", "True");
|
||||
env::set_var("FALSE_BOOL", "false");
|
||||
|
||||
config! {
|
||||
T_BOOL: bool,
|
||||
TRUE_BOOL: bool,
|
||||
NUM_BOOL: bool,
|
||||
ON_BOOL: bool,
|
||||
CAMEL_CASE: bool,
|
||||
FALSE_BOOL: bool,
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::T_BOOL(), true);
|
||||
assert_eq!(config::TRUE_BOOL(), true);
|
||||
assert_eq!(config::NUM_BOOL(), true);
|
||||
assert_eq!(config::ON_BOOL(), true);
|
||||
assert_eq!(config::CAMEL_CASE(), true);
|
||||
assert_eq!(config::FALSE_BOOL(), false);
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::T_BOOL(), true);
|
||||
assert_eq!(cfg::TRUE_BOOL(), true);
|
||||
assert_eq!(cfg::NUM_BOOL(), true);
|
||||
assert_eq!(cfg::ON_BOOL(), true);
|
||||
assert_eq!(cfg::CAMEL_CASE(), true);
|
||||
assert_eq!(cfg::FALSE_BOOL(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_number_type_value_from_env() {
|
||||
env::set_var("I8", "10");
|
||||
env::set_var("I16", "10");
|
||||
env::set_var("I32", "10");
|
||||
env::set_var("I64", "10");
|
||||
env::set_var("I128", "10");
|
||||
env::set_var("ISIZE","10");
|
||||
env::set_var("U8", "10");
|
||||
env::set_var("U16", "10");
|
||||
env::set_var("U32", "10");
|
||||
env::set_var("U64", "10");
|
||||
env::set_var("U128", "10");
|
||||
env::set_var("USIZE","10");
|
||||
env::set_var("F32", "10");
|
||||
env::set_var("F64","10");
|
||||
|
||||
config! {
|
||||
mod test_case_7 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
I8: i8,
|
||||
I16: i16,
|
||||
I32: i32,
|
||||
|
@ -129,40 +125,59 @@ fn convert_number_type_value_from_env() {
|
|||
F64: f64,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::I8(), 10);
|
||||
assert_eq!(cfg::I16(), 10);
|
||||
assert_eq!(cfg::I32(), 10);
|
||||
assert_eq!(cfg::I64(), 10);
|
||||
assert_eq!(cfg::ISIZE(), 10);
|
||||
assert_eq!(cfg::U8(), 10);
|
||||
assert_eq!(cfg::U16(), 10);
|
||||
assert_eq!(cfg::U32(), 10);
|
||||
assert_eq!(cfg::U64(), 10);
|
||||
assert_eq!(cfg::USIZE(), 10);
|
||||
assert_eq!(cfg::F32(), 10.0);
|
||||
assert_eq!(cfg::F64(), 10.0);
|
||||
#[test]
|
||||
fn convert_number_type_value_from_env() {
|
||||
env::set_var("I8", "10");
|
||||
env::set_var("I16", "10");
|
||||
env::set_var("I32", "10");
|
||||
env::set_var("I64", "10");
|
||||
env::set_var("I128", "10");
|
||||
env::set_var("ISIZE", "10");
|
||||
env::set_var("U8", "10");
|
||||
env::set_var("U16", "10");
|
||||
env::set_var("U32", "10");
|
||||
env::set_var("U64", "10");
|
||||
env::set_var("U128", "10");
|
||||
env::set_var("USIZE", "10");
|
||||
env::set_var("F32", "10");
|
||||
env::set_var("F64", "10");
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::I8(), 10);
|
||||
assert_eq!(config::I16(), 10);
|
||||
assert_eq!(config::I32(), 10);
|
||||
assert_eq!(config::I64(), 10);
|
||||
assert_eq!(config::ISIZE(), 10);
|
||||
assert_eq!(config::U8(), 10);
|
||||
assert_eq!(config::U16(), 10);
|
||||
assert_eq!(config::U32(), 10);
|
||||
assert_eq!(config::U64(), 10);
|
||||
assert_eq!(config::USIZE(), 10);
|
||||
assert_eq!(config::F32(), 10.0);
|
||||
assert_eq!(config::F64(), 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn change_configuration_module_name() {
|
||||
config! {
|
||||
#![mod_name = custom_config_name]
|
||||
mod test_case_8 {
|
||||
itconfig::config! {
|
||||
#![config(name = "custom_config_name")]
|
||||
|
||||
DEBUG: bool => true,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_configuration_module_name() {
|
||||
custom_config_name::init();
|
||||
assert_eq!(custom_config_name::DEBUG(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn configuration_with_namespace() {
|
||||
env::set_var("DB_HOST", "t");
|
||||
mod test_case_9 {
|
||||
use std::env;
|
||||
|
||||
config! {
|
||||
itconfig::config! {
|
||||
DEBUG: bool => true,
|
||||
|
||||
DB {
|
||||
|
@ -174,14 +189,18 @@ fn configuration_with_namespace() {
|
|||
APP {}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::DEBUG(), true);
|
||||
assert_eq!(cfg::DB::HOST(), true);
|
||||
#[test]
|
||||
fn configuration_with_namespace() {
|
||||
env::set_var("DB_HOST", "t");
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::DEBUG(), true);
|
||||
assert_eq!(config::DB::HOST(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configuration_with_nested_namespaces() {
|
||||
config! {
|
||||
mod test_case_10 {
|
||||
itconfig::config! {
|
||||
FIRST {
|
||||
SECOND {
|
||||
THIRD {
|
||||
|
@ -191,14 +210,16 @@ fn configuration_with_nested_namespaces() {
|
|||
}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::FIRST::SECOND::THIRD::FOO(), 50);
|
||||
#[test]
|
||||
fn configuration_with_nested_namespaces() {
|
||||
config::init();
|
||||
assert_eq!(config::FIRST::SECOND::THIRD::FOO(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "meta_namespace")]
|
||||
#[test]
|
||||
fn configuration_namespaces_with_custom_meta() {
|
||||
config! {
|
||||
|
||||
mod test_case_11 {
|
||||
itconfig::config! {
|
||||
FIRST {
|
||||
#[cfg(feature = "meta_namespace")]
|
||||
SECOND {
|
||||
|
@ -209,16 +230,19 @@ fn configuration_namespaces_with_custom_meta() {
|
|||
}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::FIRST::SECOND::THIRD::FOO(), 50);
|
||||
#[cfg(feature = "meta_namespace")]
|
||||
#[test]
|
||||
fn configuration_namespaces_with_custom_meta() {
|
||||
config::init();
|
||||
assert_eq!(config::FIRST::SECOND::THIRD::FOO(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configuration_variables_and_namespace_in_lowercase() {
|
||||
env::set_var("TESTING", "t");
|
||||
env::set_var("NAMESPACE_FOO", "t");
|
||||
|
||||
config! {
|
||||
mod test_case_12 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
testing: bool,
|
||||
|
||||
namespace {
|
||||
|
@ -226,17 +250,22 @@ fn configuration_variables_and_namespace_in_lowercase() {
|
|||
}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::testing(), true);
|
||||
assert_eq!(cfg::namespace::foo(), true);
|
||||
#[test]
|
||||
fn configuration_variables_and_namespace_in_lowercase() {
|
||||
env::set_var("TESTING", "t");
|
||||
env::set_var("NAMESPACE_FOO", "t");
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::testing(), true);
|
||||
assert_eq!(config::namespace::foo(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn custom_environment_name_for_variable() {
|
||||
env::set_var("MY_CUSTOM_NAME", "95");
|
||||
mod test_case_13 {
|
||||
use std::env;
|
||||
|
||||
config! {
|
||||
itconfig::config! {
|
||||
#[env_name = "MY_CUSTOM_NAME"]
|
||||
PER_PAGE: i32,
|
||||
|
||||
|
@ -246,16 +275,21 @@ fn custom_environment_name_for_variable() {
|
|||
}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::PER_PAGE(), 95);
|
||||
assert_eq!(cfg::APP::RECIPES_PER_PAGE(), 95);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stranger_meta_data() {
|
||||
#[test]
|
||||
fn custom_environment_name_for_variable() {
|
||||
env::set_var("MY_CUSTOM_NAME", "95");
|
||||
|
||||
config! {
|
||||
config::init();
|
||||
assert_eq!(config::PER_PAGE(), 95);
|
||||
assert_eq!(config::APP::RECIPES_PER_PAGE(), 95);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mod test_case_14 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
#[cfg(feature = "postgres")]
|
||||
#[env_name = "MY_CUSTOM_NAME"]
|
||||
DATABASE_URL: String,
|
||||
|
@ -265,40 +299,47 @@ fn stranger_meta_data() {
|
|||
DATABASE_URL: i32,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
#[test]
|
||||
fn stranger_meta_data() {
|
||||
env::set_var("MY_CUSTOM_NAME", "95");
|
||||
|
||||
config::init();
|
||||
#[cfg(not(feature = "postgres"))]
|
||||
assert_eq!(cfg::DATABASE_URL(), 95);
|
||||
assert_eq!(config::DATABASE_URL(), 95);
|
||||
|
||||
#[cfg(feature = "postgres")]
|
||||
assert_eq!(cfg::DATABASE_URL(), "95");
|
||||
assert_eq!(config::DATABASE_URL(), "95");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn setting_default_env_variable() {
|
||||
config! {
|
||||
|
||||
mod test_case_15 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
DEFAULT_ENV_STRING: String => "localhost",
|
||||
DEFAULT_ENV_BOOLEAN: bool => true,
|
||||
DEFAULT_ENV_UINT: u32 => 40,
|
||||
DEFAULT_ENV_FLOAT: f64 => 40.9,
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
|
||||
#[test]
|
||||
fn setting_default_env_variable() {
|
||||
config::init();
|
||||
|
||||
assert_eq!(env::var("DEFAULT_ENV_STRING"), Ok("localhost".to_string()));
|
||||
assert_eq!(env::var("DEFAULT_ENV_BOOLEAN"), Ok("true".to_string()));
|
||||
assert_eq!(env::var("DEFAULT_ENV_UINT"), Ok("40".to_string()));
|
||||
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");
|
||||
mod test_case_16 {
|
||||
use std::env;
|
||||
|
||||
config! {
|
||||
itconfig::config! {
|
||||
DATABASE_URL < (
|
||||
"postgres://",
|
||||
POSTGRES_USERNAME,
|
||||
|
@ -311,16 +352,23 @@ fn concatenate_environment_variables() {
|
|||
),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
assert_eq!(cfg::DATABASE_URL(), String::from("postgres://user:pass@localhost/test"));
|
||||
#[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::init();
|
||||
assert_eq!(config::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");
|
||||
mod test_case_17 {
|
||||
use std::env;
|
||||
|
||||
config! {
|
||||
itconfig::config! {
|
||||
DEFAULT_CONCAT_ENV < (
|
||||
"string",
|
||||
"/",
|
||||
|
@ -328,15 +376,19 @@ fn setting_default_concat_env_variable() {
|
|||
),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
|
||||
#[test]
|
||||
fn setting_default_concat_env_variable() {
|
||||
env::set_var("SETTING_DEFAULT_CONCAT_ENV_VARIABLE", "custom");
|
||||
|
||||
config::init();
|
||||
assert_eq!(env::var("DEFAULT_CONCAT_ENV"), Ok("string/custom".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"PG_USERNAME\" is missing")]
|
||||
fn concatenate_not_defined_environment_variables() {
|
||||
config! {
|
||||
mod test_case_18 {
|
||||
itconfig::config! {
|
||||
DATABASE_URL < (
|
||||
"postgres://",
|
||||
PG_USERNAME,
|
||||
|
@ -348,13 +400,19 @@ fn concatenate_not_defined_environment_variables() {
|
|||
PG_DB,
|
||||
),
|
||||
}
|
||||
cfg::init();
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Environment variable \"PG_USERNAME\" is missing")]
|
||||
fn concatenate_not_defined_environment_variables() {
|
||||
config::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn default_value_for_concatenate_env_parameter() {
|
||||
config! {
|
||||
mod test_case_19 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
CONCATENATED_DATABASE_URL < (
|
||||
"postgres://",
|
||||
NOT_DEFINED_PG_USERNAME => "user",
|
||||
|
@ -367,16 +425,23 @@ fn default_value_for_concatenate_env_parameter() {
|
|||
),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
|
||||
#[test]
|
||||
fn default_value_for_concatenate_env_parameter() {
|
||||
config::init();
|
||||
assert_eq!(
|
||||
env::var("CONCATENATED_DATABASE_URL"),
|
||||
Ok("postgres://user:pass@localhost:5432/test".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn envname_meta_for_concatenated_env_variable() {
|
||||
config! {
|
||||
|
||||
mod test_case_20 {
|
||||
use std::env;
|
||||
use std::env::VarError;
|
||||
|
||||
itconfig::config! {
|
||||
#[env_name = "CUSTOM_CONCAT_ENVNAME"]
|
||||
CONCAT_ENVVAR < (
|
||||
"postgres://",
|
||||
|
@ -390,17 +455,23 @@ fn envname_meta_for_concatenated_env_variable() {
|
|||
),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
#[test]
|
||||
fn envname_meta_for_concatenated_env_variable() {
|
||||
config::init();
|
||||
assert_eq!(
|
||||
env::var("CUSTOM_CONCAT_ENVNAME"),
|
||||
Ok("postgres://user:pass@localhost:5432/test".to_string())
|
||||
);
|
||||
assert_eq!(env::var("CONCAT_ENVVAR"), Err(VarError::NotPresent));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concatenated_environment_variable_in_namespace() {
|
||||
config! {
|
||||
|
||||
mod test_case_21 {
|
||||
use std::env;
|
||||
use std::env::VarError;
|
||||
|
||||
itconfig::config! {
|
||||
CONCATED_NAMESPACE {
|
||||
CONCAT_ENVVAR < (
|
||||
"postgres://",
|
||||
|
@ -415,19 +486,21 @@ fn concatenated_environment_variable_in_namespace() {
|
|||
}
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
|
||||
#[test]
|
||||
fn concatenated_environment_variable_in_namespace() {
|
||||
config::init();
|
||||
assert_eq!(
|
||||
env::var("CONCATED_NAMESPACE_CONCAT_ENVVAR"),
|
||||
Ok("postgres://user:pass@localhost:5432/test".to_string())
|
||||
);
|
||||
assert_eq!(env::var("CONCAT_ENVVAR"), Err(VarError::NotPresent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "static")]
|
||||
fn static_variables() {
|
||||
config! {
|
||||
mod test_case_22 {
|
||||
itconfig::config! {
|
||||
static STATIC_STR => "test",
|
||||
static STATIC_STRING: String => "test",
|
||||
static STATIC_I8: i8 => 1,
|
||||
|
@ -450,23 +523,27 @@ fn static_variables() {
|
|||
),
|
||||
}
|
||||
|
||||
cfg::init();
|
||||
|
||||
assert_eq!(cfg::STATIC_STR(), "test");
|
||||
assert_eq!(cfg::STATIC_STRING(), "test".to_string());
|
||||
assert_eq!(cfg::STATIC_I8(), 1);
|
||||
assert_eq!(cfg::STATIC_I16(), 1);
|
||||
assert_eq!(cfg::STATIC_I32(), 1);
|
||||
assert_eq!(cfg::STATIC_I64(), 1);
|
||||
assert_eq!(cfg::STATIC_I128(), 1);
|
||||
assert_eq!(cfg::STATIC_ISIZE(), 1);
|
||||
assert_eq!(cfg::STATIC_U8(), 1);
|
||||
assert_eq!(cfg::STATIC_U16(), 1);
|
||||
assert_eq!(cfg::STATIC_U32(), 1);
|
||||
assert_eq!(cfg::STATIC_U64(), 1);
|
||||
assert_eq!(cfg::STATIC_U128(), 1);
|
||||
assert_eq!(cfg::STATIC_USIZE(), 1);
|
||||
assert_eq!(cfg::STATIC_F32(), 1.0);
|
||||
assert_eq!(cfg::STATIC_F64(), 1.0);
|
||||
assert_eq!(cfg::STATIC_CONCAT_VARIABLE(), "static part".to_string())
|
||||
#[test]
|
||||
fn static_variables() {
|
||||
config::init();
|
||||
|
||||
assert_eq!(config::STATIC_STR(), "test");
|
||||
assert_eq!(config::STATIC_STRING(), "test".to_string());
|
||||
assert_eq!(config::STATIC_I8(), 1);
|
||||
assert_eq!(config::STATIC_I16(), 1);
|
||||
assert_eq!(config::STATIC_I32(), 1);
|
||||
assert_eq!(config::STATIC_I64(), 1);
|
||||
assert_eq!(config::STATIC_I128(), 1);
|
||||
assert_eq!(config::STATIC_ISIZE(), 1);
|
||||
assert_eq!(config::STATIC_U8(), 1);
|
||||
assert_eq!(config::STATIC_U16(), 1);
|
||||
assert_eq!(config::STATIC_U32(), 1);
|
||||
assert_eq!(config::STATIC_U64(), 1);
|
||||
assert_eq!(config::STATIC_U128(), 1);
|
||||
assert_eq!(config::STATIC_USIZE(), 1);
|
||||
assert_eq!(config::STATIC_F32(), 1.0);
|
||||
assert_eq!(config::STATIC_F64(), 1.0);
|
||||
assert_eq!(config::STATIC_CONCAT_VARIABLE(), "static part".to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "itconfig"
|
||||
version = "0.11.2"
|
||||
version = "1.0.0"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
description = "Easy build a configs from environment variables and use it in globally."
|
||||
categories = ["config", "web-programming"]
|
||||
|
@ -15,15 +15,17 @@ readme = "README.md"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
failure = { version = "0.1.6", features = ["derive"]}
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
failure = { version = "0.1.7", features = ["derive"]}
|
||||
serde_json = { version = "1.0.44", optional = true }
|
||||
itconfig-macro = { path = "../itconfig-macro", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[features]
|
||||
default = ["macro", "primitives", "static"]
|
||||
default = ["primitives"]
|
||||
|
||||
macro = []
|
||||
static = ["lazy_static"]
|
||||
macro = ["itconfig-macro"]
|
||||
|
||||
array = ["serde_json"]
|
||||
|
||||
|
|
|
@ -5,10 +5,29 @@ Easy build a configs from environment variables and use it in globally.
|
|||
We recommend you start with the [documentation].
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
These macros require a Rust compiler version 1.31 or newer.
|
||||
|
||||
Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`.
|
||||
|
||||
`Cargo.toml` example:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "my-crate"
|
||||
version = "0.1.0"
|
||||
authors = ["Me <user@rust-lang.org>"]
|
||||
|
||||
[dependencies]
|
||||
itconfig = { version = "1.0", features = ["macro"] }
|
||||
```
|
||||
|
||||
|
||||
## Example usage
|
||||
|
||||
```rust
|
||||
#[macro_use] extern crate itconfig;
|
||||
use std::itconfig;
|
||||
use std::env;
|
||||
//use dotenv::dotenv;
|
||||
|
||||
|
@ -56,11 +75,11 @@ fn main () {
|
|||
// dotenv().ok();
|
||||
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::APP:ARTICLE:PER_PAGE(), 15);
|
||||
assert_eq!(cfg::FEATURE::NEW_MENU(), true);
|
||||
config::init();
|
||||
assert_eq!(config::HOST(), String::from("127.0.0.1"));
|
||||
assert_eq!(config::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test"));
|
||||
assert_eq!(config::APP:ARTICLE:PER_PAGE(), 15);
|
||||
assert_eq!(config::FEATURE::NEW_MENU(), true);
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -93,6 +112,7 @@ fn main() {
|
|||
* [x] Add nested namespaces
|
||||
* [x] Support meta for namespaces
|
||||
* [x] Support array type
|
||||
* [x] Rewrite to proc macro
|
||||
* [ ] Support hashmap type
|
||||
* [ ] Support custom env type
|
||||
* [ ] Common configuration for namespace variables
|
||||
|
@ -102,7 +122,6 @@ fn main() {
|
|||
|
||||
* **default** - ["macro", "primitives", "static"]
|
||||
* **macro** - Activates `config!` macros for easy configure web application.
|
||||
* **static** - Add `static` option to `config!` macros (uses optional `lazy_static` package).
|
||||
* **array** - Add EnvString impl for vector type (uses optional `serde_json` package).
|
||||
* **primitives** - Group for features: `numbers` and `bool`.
|
||||
* **numbers** - Group for features: `int`, `uint` and `float`.
|
||||
|
|
33
itconfig/src/cfg.rs
Normal file
33
itconfig/src/cfg.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
config! {
|
||||
#![config(unwrap)]
|
||||
|
||||
TEST => "hello",
|
||||
static MAIN => "main",
|
||||
TEST2 => "test",
|
||||
|
||||
CONCAT < (
|
||||
TESTTTT => "hellooooooo",
|
||||
" ",
|
||||
"world",
|
||||
),
|
||||
|
||||
NAMESPACE {
|
||||
TEST: String => "test",
|
||||
|
||||
NAMESPACE {
|
||||
TEST: &'static str => "test",
|
||||
|
||||
#[env_prefix = "HELLO_"]
|
||||
NAMESPACE {
|
||||
TEST: &'static str => "test",
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[env_prefix = "WORLD_"]
|
||||
NAMESPACE {
|
||||
#[env_name = "TEST_TEST_TEST"]
|
||||
TEST: &'static str => "test",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,10 +12,30 @@
|
|||
//! as application lifetime. Because of it I decided to create my own library.
|
||||
//!
|
||||
//!
|
||||
//! ## Installation
|
||||
//!
|
||||
//! These macros require a Rust compiler version 1.31 or newer.
|
||||
//!
|
||||
//! Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`.
|
||||
//!
|
||||
//!
|
||||
//! `Cargo.toml` example:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [package]
|
||||
//! name = "my-crate"
|
||||
//! version = "0.1.0"
|
||||
//! authors = ["Me <user@rust-lang.org>"]
|
||||
//!
|
||||
//! [dependencies]
|
||||
//! itconfig = { version = "1.0", features = ["macro"] }
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! ## Example usage
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use] extern crate itconfig;
|
||||
//! use itconfig::config;
|
||||
//! use std::env;
|
||||
//! // use dotenv::dotenv;
|
||||
//!
|
||||
|
@ -61,12 +81,12 @@
|
|||
//! // dotenv().ok();
|
||||
//! 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::APP::BASE_URL(), "/api");
|
||||
//! assert_eq!(cfg::APP::ARTICLE::PER_PAGE(), 15);
|
||||
//! assert_eq!(cfg::FEATURE::NEW_MENU(), true);
|
||||
//! config::init();
|
||||
//! assert_eq!(config::HOST(), String::from("127.0.0.1"));
|
||||
//! assert_eq!(config::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test"));
|
||||
//! assert_eq!(config::APP::BASE_URL(), "/api");
|
||||
//! assert_eq!(config::APP::ARTICLE::PER_PAGE(), 15);
|
||||
//! assert_eq!(config::FEATURE::NEW_MENU(), true);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
@ -89,9 +109,8 @@
|
|||
//!
|
||||
//! ## Available features
|
||||
//!
|
||||
//! * **default** - ["macro", "primitives", "static"]
|
||||
//! * **default** - ["primitives"]
|
||||
//! * **macro** - Activates `config!` macros for easy configure web application.
|
||||
//! * **static** - Add `static` option to `config!` macros (uses optional `lazy_static` package).
|
||||
//! * **array** - Add EnvString impl for vector type (uses optional `serde_json` package).
|
||||
//! * **primitives** - Group for features: `numbers` and `bool`.
|
||||
//! * **numbers** - Group for features: `int`, `uint` and `float`.
|
||||
|
@ -129,8 +148,6 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
#[cfg(feature = "static")]
|
||||
pub extern crate lazy_static;
|
||||
|
||||
mod enverr;
|
||||
mod getenv;
|
||||
|
@ -146,9 +163,7 @@ pub mod prelude {
|
|||
|
||||
|
||||
#[cfg(feature = "macro")]
|
||||
//#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
mod r#macro;
|
||||
extern crate itconfig_macro;
|
||||
#[cfg(feature = "macro")]
|
||||
#[doc(hidden)]
|
||||
pub use r#macro::*;
|
||||
pub use itconfig_macro::*;
|
|
@ -1,880 +0,0 @@
|
|||
|
||||
/// ### _This API requires the following crate features to be activated: `macro`_
|
||||
///
|
||||
/// Creates new public mod with function fo get each environment variable of mapping.
|
||||
///
|
||||
/// All variables are required and program will panic if some variables haven't value, but you
|
||||
/// can add default value for specific variable.
|
||||
///
|
||||
/// Starts with v0.6.0 if you don't have an optional variable, the variable is set automatically.
|
||||
///
|
||||
/// Example usage
|
||||
/// -------------
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// # env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
|
||||
/// config! {
|
||||
/// DATABASE_URL: String,
|
||||
/// }
|
||||
/// # cfg::init()
|
||||
/// ```
|
||||
///
|
||||
/// Config with default value
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// # env::set_var("DATABASE_URL", "postgres://u:p@localhost:5432/db");
|
||||
/// config! {
|
||||
/// DATABASE_URL: String,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
/// # cfg::init()
|
||||
/// ```
|
||||
///
|
||||
/// By default itconfig lib creates module with 'cfg' name. But you can use simple meta instruction
|
||||
/// if you want to rename module. In the example below we renamed module to 'configuration'
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// env::set_var("DEBUG", "t");
|
||||
///
|
||||
/// config! {
|
||||
/// #![mod_name = configuration]
|
||||
///
|
||||
/// DEBUG: bool,
|
||||
/// }
|
||||
///
|
||||
/// configuration::init();
|
||||
/// assert_eq!(configuration::DEBUG(), true);
|
||||
/// ```
|
||||
///
|
||||
/// Namespaces
|
||||
/// ----------
|
||||
///
|
||||
/// You can use namespaces for env variables
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// env::set_var("DEBUG", "t");
|
||||
/// env::set_var("DATABASE_USERNAME", "user");
|
||||
/// env::set_var("DATABASE_PASSWORD", "pass");
|
||||
/// env::set_var("DATABASE_HOST", "localhost");
|
||||
///
|
||||
/// config! {
|
||||
/// DEBUG: bool,
|
||||
/// DATABASE {
|
||||
/// USERNAME: String,
|
||||
/// PASSWORD: String,
|
||||
/// HOST: 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
|
||||
/// ----
|
||||
///
|
||||
/// If you want to read custom env name for variable you can change it manually.
|
||||
///
|
||||
/// **A variable in the nameespace will lose environment prefix**
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// env::set_var("MY_CUSTOM_NAME", "95");
|
||||
///
|
||||
/// config! {
|
||||
/// #[env_name = "MY_CUSTOM_NAME"]
|
||||
/// PER_PAGE: i32,
|
||||
///
|
||||
/// APP {
|
||||
/// #[env_name = "MY_CUSTOM_NAME"]
|
||||
/// RECIPES_PER_PAGE: i32,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// cfg::init();
|
||||
/// assert_eq!(cfg::PER_PAGE(), 95);
|
||||
/// assert_eq!(cfg::APP::RECIPES_PER_PAGE(), 95);
|
||||
/// ```
|
||||
///
|
||||
/// Also you can add custom meta for each variable. For example feature configurations.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// config! {
|
||||
/// #[cfg(feature = "postgres")]
|
||||
/// DATABASE_URL: String,
|
||||
///
|
||||
/// #[cfg(not(feature = "postgres"))]
|
||||
/// DATABASE_URL: String,
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// Concatenate
|
||||
/// -----------
|
||||
///
|
||||
/// Try to concatenate env variable or strings or both to you env variable. It's easy!
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// env::set_var("POSTGRES_USERNAME", "user");
|
||||
/// env::set_var("POSTGRES_PASSWORD", "pass");
|
||||
///
|
||||
/// config! {
|
||||
/// DATABASE_URL < (
|
||||
/// "postgres://",
|
||||
/// POSTGRES_USERNAME,
|
||||
/// ":",
|
||||
/// POSTGRES_PASSWORD,
|
||||
/// "@",
|
||||
/// POSTGRES_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// POSTGRES_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
///
|
||||
/// cfg::init();
|
||||
/// assert_eq!(cfg::DATABASE_URL(), "postgres://user:pass@localhost:5432/test".to_string())
|
||||
/// ```
|
||||
///
|
||||
/// Concatinated variables can be only strings and support all features like namespaces and meta.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// config! {
|
||||
/// CONCATED_NAMESPACE {
|
||||
/// #[env_name = "DATABASE_URL"]
|
||||
/// CONCAT_ENVVAR < (
|
||||
/// "postgres://",
|
||||
/// NOT_DEFINED_PG_USERNAME => "user",
|
||||
/// ":",
|
||||
/// NOT_DEFINED_PG_PASSWORD => "pass",
|
||||
/// "@",
|
||||
/// NOT_DEFINED_PG_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// NOT_DEFINED_PG_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// cfg::init();
|
||||
/// ```
|
||||
///
|
||||
/// Static
|
||||
/// ------
|
||||
///
|
||||
/// `with feauter = "static"`
|
||||
///
|
||||
/// Starting with 0.11 version you can use lazy static for improve speed of variable. This is very
|
||||
/// useful, if you use variable more than once.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// # use std::env;
|
||||
/// env::set_var("APP_BASE_URL", "/api/v1");
|
||||
///
|
||||
/// config! {
|
||||
/// static APP_BASE_URL => "/api",
|
||||
/// }
|
||||
///
|
||||
/// cfg::init();
|
||||
/// assert_eq!(cfg::APP_BASE_URL(), "/api/v1");
|
||||
/// ```
|
||||
///
|
||||
/// You also can use static with concat variables
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// config! {
|
||||
/// static CONNECTION_STRING < (
|
||||
/// "postgres://",
|
||||
/// NOT_DEFINED_PG_USERNAME => "user",
|
||||
/// ":",
|
||||
/// NOT_DEFINED_PG_PASSWORD => "pass",
|
||||
/// "@",
|
||||
/// NOT_DEFINED_PG_HOST => "localhost:5432",
|
||||
/// "/",
|
||||
/// NOT_DEFINED_PG_DB => "test",
|
||||
/// ),
|
||||
/// }
|
||||
///
|
||||
/// cfg::init();
|
||||
/// assert_eq!(cfg::CONNECTION_STRING(), "postgres://user:pass@localhost:5432/test".to_string());
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// This module will also contain helper method:
|
||||
/// --------------------------------------------
|
||||
///
|
||||
/// ```rust
|
||||
/// pub fn init() {}
|
||||
/// ```
|
||||
///
|
||||
/// Run this at the main function for check all required variables without default value.
|
||||
///
|
||||
/// Panics
|
||||
/// ------
|
||||
///
|
||||
/// If you miss some required variables your application will panic at startup.
|
||||
///
|
||||
/// Examples
|
||||
/// --------
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate itconfig;
|
||||
/// // use dotenv::dotenv;
|
||||
///
|
||||
/// config! {
|
||||
/// DEBUG: bool => true,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// // dotenv().ok();
|
||||
/// cfg::init();
|
||||
/// assert_eq!(cfg::HOST(), String::from("127.0.0.1"));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! config {
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_parse_module! {
|
||||
tokens = [$($tokens)*],
|
||||
name = cfg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_invalid_syntax {
|
||||
() => {
|
||||
compile_error!(
|
||||
"Invalid `config!` syntax. Please see the `config!` macro docs for more info.\
|
||||
`https://docs.rs/itconfig/latest/itconfig/macro.config.html`"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_parse_module {
|
||||
// Find module name
|
||||
(
|
||||
tokens = [
|
||||
#![mod_name = $mod_name:ident]
|
||||
$($rest:tt)*
|
||||
],
|
||||
name = $ignore:tt,
|
||||
) => {
|
||||
__itconfig_parse_module! {
|
||||
tokens = [$($rest)*],
|
||||
name = $mod_name,
|
||||
}
|
||||
};
|
||||
|
||||
// Done parsing module
|
||||
(
|
||||
tokens = $tokens:tt,
|
||||
name = $name:tt,
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
tokens = $tokens,
|
||||
variables = [],
|
||||
namespaces = [],
|
||||
module = {
|
||||
env_prefix = "",
|
||||
name = $name,
|
||||
meta = [],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_invalid_syntax!();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "static")]
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_impl_static_feature {
|
||||
(@import_modules) => {
|
||||
use $crate::lazy_static::lazy_static;
|
||||
};
|
||||
|
||||
(
|
||||
unparsed_meta = $meta:tt,
|
||||
unparsed_concat = $concat:tt,
|
||||
name = $name:ident,
|
||||
ty = $ty:ty,
|
||||
$(default = $default:expr,)?
|
||||
tokens = $tokens:tt,
|
||||
args = [$($args:tt)*],
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
current_variable = {
|
||||
unparsed_meta = $meta,
|
||||
meta = [],
|
||||
unparsed_concat = $concat,
|
||||
concat = [],
|
||||
name = $name,
|
||||
ty = $ty,
|
||||
is_static = true,
|
||||
$(default = $default,)?
|
||||
},
|
||||
tokens = $tokens,
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "static"))]
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_impl_static_feature {
|
||||
(@import_modules) => { };
|
||||
|
||||
($($tt:tt)*) => {
|
||||
compile_error!(
|
||||
"Feature `static` is required for enable this macro function.\
|
||||
Please see the `config!` macro docs for more info.\
|
||||
`https://docs.rs/itconfig/latest/itconfig/macro.config.html`"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_get_ty_or_default {
|
||||
() => { &'static str };
|
||||
($ty:ty) => { $ty };
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_parse_variables {
|
||||
// Find namespace
|
||||
(
|
||||
tokens = [
|
||||
$(#$meta:tt)*
|
||||
$ns_name:ident { $($ns_tokens:tt)* }
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
tokens = [$($ns_tokens)*],
|
||||
variables = [],
|
||||
namespaces = [],
|
||||
module = {
|
||||
env_prefix = concat!(stringify!($ns_name), "_"),
|
||||
name = $ns_name,
|
||||
meta = [$(#$meta)*],
|
||||
},
|
||||
callback = {
|
||||
tokens = [$($rest)*],
|
||||
$($args)*
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Find static concatenated variable
|
||||
(
|
||||
tokens = [
|
||||
$(#$meta:tt)*
|
||||
static $name:ident < ($($inner:tt)+),
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_impl_static_feature! {
|
||||
unparsed_meta = [$(#$meta)*],
|
||||
unparsed_concat = [$($inner)+],
|
||||
name = $name,
|
||||
ty = String,
|
||||
tokens = [$($rest)*],
|
||||
args = [$($args)*],
|
||||
}
|
||||
};
|
||||
|
||||
// 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,
|
||||
is_static = false,
|
||||
},
|
||||
tokens = [$($rest)*],
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Find static variable
|
||||
(
|
||||
tokens = [
|
||||
$(#$meta:tt)*
|
||||
static $name:ident $(: $ty:ty)? $(=> $default:expr)?,
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_impl_static_feature! {
|
||||
unparsed_meta = [$(#$meta)*],
|
||||
unparsed_concat = [],
|
||||
name = $name,
|
||||
ty = __itconfig_get_ty_or_default!($($ty)?),
|
||||
$(default = $default,)?
|
||||
tokens = [$($rest)*],
|
||||
args = [$($args)*],
|
||||
}
|
||||
};
|
||||
|
||||
// Find variable
|
||||
(
|
||||
tokens = [
|
||||
$(#$meta:tt)*
|
||||
$name:ident $(: $ty:ty)? $(=> $default:expr)?,
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
current_variable = {
|
||||
unparsed_meta = [$(#$meta)*],
|
||||
meta = [],
|
||||
unparsed_concat = [],
|
||||
concat = [],
|
||||
name = $name,
|
||||
ty = __itconfig_get_ty_or_default!($($ty)?),
|
||||
is_static = false,
|
||||
$(default = $default,)?
|
||||
},
|
||||
tokens = [$($rest)*],
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Find meta with custom env name
|
||||
(
|
||||
current_variable = {
|
||||
unparsed_meta = [
|
||||
#[env_name = $env_name:expr]
|
||||
$($rest:tt)*
|
||||
],
|
||||
meta = $meta:tt,
|
||||
unparsed_concat = $unparsed_concat:tt,
|
||||
concat = $concat:tt,
|
||||
name = $name:ident,
|
||||
$($current_variable:tt)*
|
||||
},
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
current_variable = {
|
||||
unparsed_meta = [$($rest)*],
|
||||
meta = $meta,
|
||||
unparsed_concat = $unparsed_concat,
|
||||
concat = $concat,
|
||||
name = $name,
|
||||
env_name = $env_name,
|
||||
$($current_variable)*
|
||||
},
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Find stranger meta
|
||||
(
|
||||
current_variable = {
|
||||
unparsed_meta = [
|
||||
#$stranger_meta:tt
|
||||
$($rest:tt)*
|
||||
],
|
||||
meta = [$(#$meta:tt,)*],
|
||||
$($current_variable:tt)*
|
||||
},
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
current_variable = {
|
||||
unparsed_meta = [$($rest)*],
|
||||
meta = [$(#$meta,)* #$stranger_meta,],
|
||||
$($current_variable)*
|
||||
},
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Parse concat params
|
||||
(
|
||||
current_variable = {
|
||||
unparsed_meta = $unparsed_meta:tt,
|
||||
meta = $meta:tt,
|
||||
unparsed_concat = [
|
||||
$concat_param:tt$( => $default:expr)?,
|
||||
$($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$( => $default)?),],
|
||||
$($current_variable)*
|
||||
},
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Done parsing variable
|
||||
(
|
||||
current_variable = {
|
||||
unparsed_meta = [],
|
||||
meta = $meta:tt,
|
||||
unparsed_concat = [],
|
||||
$($current_variable:tt)*
|
||||
},
|
||||
tokens = $tokens:tt,
|
||||
variables = [$($variables:tt,)*],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
tokens = $tokens,
|
||||
variables = [$($variables,)* { meta = $meta, $($current_variable)* },],
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Done parsing all variables of namespace
|
||||
(
|
||||
tokens = [],
|
||||
variables = $ns_variables:tt,
|
||||
namespaces = $ns_namespaces:tt,
|
||||
module = $ns_module:tt,
|
||||
callback = {
|
||||
tokens = $tokens:tt,
|
||||
variables = $variables:tt,
|
||||
namespaces = [$($namespaces:tt,)*],
|
||||
$($args:tt)*
|
||||
},
|
||||
) => {
|
||||
__itconfig_parse_variables! {
|
||||
tokens = $tokens,
|
||||
variables = $variables,
|
||||
namespaces = [
|
||||
$($namespaces,)*
|
||||
{
|
||||
variables = $ns_variables,
|
||||
namespaces = $ns_namespaces,
|
||||
module = $ns_module,
|
||||
},
|
||||
],
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Done parsing all variables
|
||||
(
|
||||
tokens = [],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_impl_namespace!($($args)*);
|
||||
};
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_invalid_syntax!();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_impl_namespace {
|
||||
(
|
||||
variables = [$({
|
||||
meta = $var_meta:tt,
|
||||
concat = $var_concat:tt,
|
||||
name = $var_name:ident,
|
||||
$(env_name = $env_name:expr,)?
|
||||
ty = $ty:ty,
|
||||
is_static = $is_static:ident,
|
||||
$($variable:tt)*
|
||||
},)*],
|
||||
namespaces = [$({
|
||||
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)]
|
||||
|
||||
__itconfig_impl_static_feature!( @import_modules );
|
||||
|
||||
$(__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_meta)*
|
||||
$ns_mod_name::init();
|
||||
)*
|
||||
}
|
||||
|
||||
$(__itconfig_variable! {
|
||||
meta = $var_meta,
|
||||
concat = $var_concat,
|
||||
name = $var_name,
|
||||
env_prefix = $env_prefix,
|
||||
$(env_name = $env_name,)?
|
||||
ty = $ty,
|
||||
is_static = $is_static,
|
||||
$($variable)*
|
||||
})*
|
||||
}
|
||||
};
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_invalid_syntax!();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __itconfig_concat_param {
|
||||
// Find env parameter with default value
|
||||
($env_name:ident => $default:expr) => (
|
||||
itconfig::get_env_or_default(
|
||||
stringify!($env_name).to_uppercase().as_str(),
|
||||
$default
|
||||
)
|
||||
);
|
||||
|
||||
// Find env parameter without default value
|
||||
($env_name:ident) => (
|
||||
itconfig::get_env_or_panic(stringify!($env_name).to_uppercase().as_str())
|
||||
);
|
||||
|
||||
// Find string parameter
|
||||
($str:expr) => ( $str.to_string() );
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_invalid_syntax!();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[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,
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__itconfig_variable! {
|
||||
meta = $meta,
|
||||
concat = $concat,
|
||||
name = $name,
|
||||
env_prefix = $env_prefix,
|
||||
env_name = concat!($env_prefix, stringify!($name)).to_uppercase(),
|
||||
ty = $ty,
|
||||
$($args)*
|
||||
}
|
||||
};
|
||||
|
||||
// Add method for env variable
|
||||
(
|
||||
meta = $meta:tt,
|
||||
concat = $concat:tt,
|
||||
name = $name:ident,
|
||||
env_prefix = $env_prefix:expr,
|
||||
env_name = $env_name:expr,
|
||||
ty = $ty:ty,
|
||||
is_static = $is_static:ident,
|
||||
$(default = $default:expr,)?
|
||||
) => {
|
||||
__itconfig_variable!(
|
||||
@wrap
|
||||
is_static = $is_static,
|
||||
meta = $meta,
|
||||
name = $name,
|
||||
ty = $ty,
|
||||
value = __itconfig_variable!(
|
||||
@inner
|
||||
concat = $concat,
|
||||
env_name = $env_name,
|
||||
$(default = $default,)?
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// Wrap static variables
|
||||
(
|
||||
@wrap
|
||||
is_static = true,
|
||||
meta = [$(#$meta:tt,)*],
|
||||
name = $name:ident,
|
||||
ty = $ty:ty,
|
||||
value = $value:expr,
|
||||
) => (
|
||||
$(#$meta)*
|
||||
pub fn $name() -> $ty {
|
||||
lazy_static! {
|
||||
static ref $name: $ty = $value;
|
||||
}
|
||||
|
||||
(*$name).clone()
|
||||
}
|
||||
);
|
||||
|
||||
// Wrap functions
|
||||
(
|
||||
@wrap
|
||||
is_static = false,
|
||||
meta = [$(#$meta:tt,)*],
|
||||
name = $name:ident,
|
||||
ty = $ty:ty,
|
||||
value = $value:expr,
|
||||
) => (
|
||||
$(#$meta)*
|
||||
pub fn $name() -> $ty { $value }
|
||||
);
|
||||
|
||||
// Concatenate function body
|
||||
(
|
||||
@inner
|
||||
concat = [$($concat:expr,)+],
|
||||
env_name = $env_name:expr,
|
||||
$($args:tt)*
|
||||
) => ({
|
||||
let value_parts: Vec<String> = vec!($($concat),+);
|
||||
let value = value_parts.join("");
|
||||
std::env::set_var($env_name, value.as_str());
|
||||
value
|
||||
});
|
||||
|
||||
// Env without default
|
||||
(
|
||||
@inner
|
||||
concat = [],
|
||||
env_name = $env_name:expr,
|
||||
) => (
|
||||
itconfig::get_env_or_panic($env_name.to_string().as_str())
|
||||
);
|
||||
|
||||
// Env with default
|
||||
(
|
||||
@inner
|
||||
concat = [],
|
||||
env_name = $env_name:expr,
|
||||
default = $default:expr,
|
||||
) => (
|
||||
itconfig::get_env_or_set_default(
|
||||
$env_name.to_string().as_str(),
|
||||
$default
|
||||
)
|
||||
);
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__itconfig_invalid_syntax!();
|
||||
};
|
||||
}
|
||||
|
Reference in a new issue