Initial commit
This commit is contained in:
commit
c460f11dce
7 changed files with 495 additions and 0 deletions
4
.env
Normal file
4
.env
Normal file
|
@ -0,0 +1,4 @@
|
|||
DEBUG=1
|
||||
TESTING=0
|
||||
SECRET_KEY='hello:)'
|
||||
DATABASE_URL='postgres:/'
|
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.idea/
|
||||
|
||||
/target
|
||||
Cargo.lock
|
||||
|
||||
src/main.rs
|
||||
.env*
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "itconfig"
|
||||
version = "0.1.1"
|
||||
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", "macro", "configuration", "environment"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/icetemple/itconfig-rs"
|
||||
homepage = "https://github.com/icetemple/itconfig-rs"
|
||||
documentation = "https://docs.rs/itconfig"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 IceTemple
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
27
README.md
Normal file
27
README.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# itconfig
|
||||
|
||||
Simple configuration with macro for rust application.
|
||||
|
||||
We recommend you start with the [documentation].
|
||||
|
||||
|
||||
## Example usage
|
||||
|
||||
```rust
|
||||
#[macro_use] extern crate itconfig;
|
||||
use dotenv::dotenv;
|
||||
|
||||
config! {
|
||||
DATABASE_URL: bool,
|
||||
HOST: String => "127.0.0.1",
|
||||
}
|
||||
|
||||
fn main () {
|
||||
dotenv().ok();
|
||||
cfg::init();
|
||||
assert_eq(cfg::HOST(), String::from("127.0.0.1");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
[documentation]: https://docs.rs/itconfig
|
308
src/lib.rs
Normal file
308
src/lib.rs
Normal file
|
@ -0,0 +1,308 @@
|
|||
//! # itconfig
|
||||
//!
|
||||
//! Simple configuration with macro for rust application.
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ## Example usage
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use] extern crate itconfig;
|
||||
//! use dotenv::dotenv;
|
||||
//!
|
||||
//! config! {
|
||||
//! DATABASE_URL: bool,
|
||||
//! HOST: String => "127.0.0.1",
|
||||
//! }
|
||||
//!
|
||||
//! fn main () {
|
||||
//! dotenv().ok();
|
||||
//! cfg::init();
|
||||
//! assert_eq(cfg::HOST(), String::from("127.0.0.1");
|
||||
//! }
|
||||
|
||||
#[doc(hidden)]
|
||||
macro_rules! __impl_from_for_numbers {
|
||||
(
|
||||
$($ty:ty),+
|
||||
) => {
|
||||
$(
|
||||
impl From<EnvValue> for $ty {
|
||||
fn from(env: EnvValue) -> Self {
|
||||
env.0.parse::<Self>().unwrap()
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
#[doc(hidden)]
|
||||
pub struct EnvValue(String);
|
||||
|
||||
impl EnvValue {
|
||||
pub fn new(string: String) -> Self {
|
||||
Self(string)
|
||||
}
|
||||
}
|
||||
|
||||
__impl_from_for_numbers![
|
||||
i8, i16, i32, i64, i128, isize,
|
||||
u8, u16, u32, u64, u128, usize,
|
||||
f32, f64
|
||||
];
|
||||
|
||||
impl From<EnvValue> for bool {
|
||||
fn from(env: EnvValue) -> Self {
|
||||
match env.0.to_lowercase().as_str() {
|
||||
"true" | "1" | "t" | "on" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for EnvValue {
|
||||
fn from(val: String) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EnvValue> for String {
|
||||
fn from(env: EnvValue) -> Self {
|
||||
env.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Creates new public 'cfg' 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.
|
||||
///
|
||||
/// Example usage
|
||||
/// -------------
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// config! {
|
||||
/// DATABASE_URL: bool,
|
||||
/// }
|
||||
///
|
||||
/// # fn main () {
|
||||
/// # use std::env;
|
||||
/// # env::set_var("DATABASE_URL", "sqlite://");
|
||||
/// #
|
||||
/// # cfg::init();
|
||||
/// # assert_eq(cfg::DATABASE_URL(), true);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Config with default value
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate itconfig;
|
||||
/// config! {
|
||||
/// DATABASE_URL: bool,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
///
|
||||
/// # fn main () {
|
||||
/// # cfg::init();
|
||||
/// # assert_eq(cfg::HOST(), String::from("127.0.0.1");
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This module will also contain helper method:
|
||||
///
|
||||
/// `init`
|
||||
/// ------
|
||||
///
|
||||
/// If you miss some required variables your application will panic at startup.
|
||||
/// Run this at the main function for check all required variables without default value.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate itconfig;
|
||||
///
|
||||
/// config! {
|
||||
/// DATABASE_URL: bool,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// cfg::init();
|
||||
/// assert_eq(cfg::HOST(), String::from("127.0.0.1");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Also dotenv module is supported
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate itconfig;
|
||||
/// use dotenv::dotenv;
|
||||
///
|
||||
/// config! {
|
||||
/// DATABASE_URL: bool,
|
||||
/// HOST: String => "127.0.0.1",
|
||||
/// }
|
||||
///
|
||||
/// fn main () {
|
||||
/// dotenv().ok();
|
||||
/// cfg::init();
|
||||
/// assert_eq(cfg::HOST(), String::from("127.0.0.1");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! config {
|
||||
($($tokens:tt)*) => {
|
||||
__config_parse_variables!(
|
||||
tokens = [$($tokens)*],
|
||||
variables = [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __invalid_config_syntax {
|
||||
() => {
|
||||
compile_error!(
|
||||
"Invalid `config!` syntax. Please see the `config!` macro docs for more info."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __config_parse_variables {
|
||||
// Find general config of variable
|
||||
(
|
||||
tokens = [
|
||||
$name:ident : $ty:ty => $default:expr,
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__config_parse_variables!(
|
||||
current_variable = {
|
||||
name = $name,
|
||||
ty = $ty,
|
||||
env_name = stringify!($name),
|
||||
default = $default,
|
||||
},
|
||||
tokens = [$($rest)*],
|
||||
$($args)*
|
||||
);
|
||||
};
|
||||
|
||||
(
|
||||
tokens = [
|
||||
$name:ident : $ty:ty,
|
||||
$($rest:tt)*
|
||||
],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__config_parse_variables!(
|
||||
current_variable = {
|
||||
name = $name,
|
||||
ty = $ty,
|
||||
env_name = stringify!($name),
|
||||
},
|
||||
tokens = [$($rest)*],
|
||||
$($args)*
|
||||
);
|
||||
};
|
||||
|
||||
// Done parsing variable
|
||||
(
|
||||
current_variable = {
|
||||
$($current_variable:tt)*
|
||||
},
|
||||
tokens = $tokens:tt,
|
||||
variables = [$($variables:tt,)*],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__config_parse_variables!(
|
||||
tokens = $tokens,
|
||||
variables = [$($variables,)* { $($current_variable)* },],
|
||||
);
|
||||
};
|
||||
|
||||
// Done parsing all variables
|
||||
(
|
||||
tokens = [],
|
||||
$($args:tt)*
|
||||
) => {
|
||||
__config_impl!($($args)*);
|
||||
};
|
||||
|
||||
// Invalid syntax
|
||||
($($tokens:tt)*) => {
|
||||
__invalid_config_syntax!();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __config_impl {
|
||||
(
|
||||
variables = [$({
|
||||
name = $name:ident,
|
||||
$($variable:tt)*
|
||||
},)+],
|
||||
) => {
|
||||
pub mod cfg {
|
||||
#![allow(non_snake_case)]
|
||||
use std::env;
|
||||
use $crate::EnvValue;
|
||||
|
||||
pub fn init() {
|
||||
$($name();)+
|
||||
}
|
||||
|
||||
$(__config_variable! {
|
||||
name = $name,
|
||||
$($variable)*
|
||||
})+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! __config_variable {
|
||||
// Add method with default value
|
||||
(
|
||||
name = $name:ident,
|
||||
ty = $ty:ty,
|
||||
env_name = $env_name:expr,
|
||||
default = $default:expr,
|
||||
) => {
|
||||
pub fn $name() -> $ty {
|
||||
env::var($env_name)
|
||||
.map(|val| EnvValue::from(val).into())
|
||||
.unwrap_or_else(|_| $default)
|
||||
}
|
||||
};
|
||||
|
||||
// Add method without default value
|
||||
(
|
||||
name = $name:ident,
|
||||
ty = $ty:ty,
|
||||
env_name = $env_name:expr,
|
||||
) => {
|
||||
pub fn $name() -> $ty {
|
||||
env::var($env_name)
|
||||
.map(|val| EnvValue::from(val).into())
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(format!(r#"Cannot read "{}" environment variable"#, $env_name))
|
||||
})
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
111
tests/config_macro.rs
Normal file
111
tests/config_macro.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use std::env;
|
||||
#[macro_use]
|
||||
extern crate it_config;
|
||||
|
||||
#[test]
|
||||
fn one_variable() {
|
||||
config! {
|
||||
DEBUG: bool => true,
|
||||
}
|
||||
|
||||
assert_eq!(cfg::DEBUG(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn few_variables() {
|
||||
config! {
|
||||
FOO: bool => true,
|
||||
BAR: bool => false,
|
||||
}
|
||||
|
||||
assert_eq!(cfg::FOO(), true);
|
||||
assert_eq!(cfg::BAR(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_types() {
|
||||
config! {
|
||||
NUMBER: i32 => 30,
|
||||
BOOL: bool => true,
|
||||
STRING: String => "string".to_string(),
|
||||
}
|
||||
|
||||
assert_eq!(cfg::NUMBER(), 30);
|
||||
assert_eq!(cfg::BOOL(), true);
|
||||
assert_eq!(cfg::STRING(), "string");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_bool_type_from_env() {
|
||||
env::set_var("T_BOOL", "t");
|
||||
env::set_var("TRUE_BOOL", "true");
|
||||
env::set_var("NUM_BOOL", "1");
|
||||
env::set_var("ON_BOOL", "on");
|
||||
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,
|
||||
|
||||
}
|
||||
|
||||
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_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! {
|
||||
I8: i8,
|
||||
I16: i16,
|
||||
I32: i32,
|
||||
I64: i64,
|
||||
I128: i128,
|
||||
ISIZE: isize,
|
||||
U8: u8,
|
||||
U16: u16,
|
||||
U32: u32,
|
||||
U64: u64,
|
||||
U128: u128,
|
||||
USIZE: usize,
|
||||
F32: f32,
|
||||
F64: f64,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
Reference in a new issue