refac: change structure
- core/vec: rename envstring to estring - tests: improve test cases + created helper TestCase + move tests that required a feature to a separate module
This commit is contained in:
parent
9448c26337
commit
bc7ab940b0
14 changed files with 228 additions and 193 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -194,6 +194,7 @@ dependencies = [
|
||||||
name = "itconfig"
|
name = "itconfig"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"criterion",
|
||||||
"itconfig-macro",
|
"itconfig-macro",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
@ -209,15 +210,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itconfig_tests"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"criterion",
|
|
||||||
"itconfig",
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
|
41
Cargo.toml
41
Cargo.toml
|
@ -1,7 +1,44 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"itconfig",
|
|
||||||
"itconfig-macro",
|
"itconfig-macro",
|
||||||
"itconfig-tests",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "itconfig"
|
||||||
|
version = "1.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", "configuration", "environment", "macro"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/pleshevskiy/itconfig-rs"
|
||||||
|
homepage = "https://github.com/pleshevskiy/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
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
macro = ["itconfig-macro"]
|
||||||
|
|
||||||
|
number = []
|
||||||
|
bool = []
|
||||||
|
vec = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itconfig-macro = { version = "1.1", path = "itconfig-macro", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
criterion = "0.3.1"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
maintenance = { status = "actively-developed" }
|
||||||
|
|
||||||
|
# https://docs.rs/about
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ quote = "1.0.9"
|
||||||
proc-macro2 = "1.0.24"
|
proc-macro2 = "1.0.24"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
itconfig = { path = "../itconfig" }
|
itconfig = { path = ".." }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "itconfig_tests"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
|
||||||
edition = "2018"
|
|
||||||
license = "MIT"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itconfig = { path = '../itconfig', features = ["macro"] }
|
|
||||||
criterion = "0.3.1"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["meta_namespace"]
|
|
||||||
meta_namespace = []
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "main_benches"
|
|
||||||
harness = false
|
|
|
@ -1,38 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "itconfig"
|
|
||||||
version = "1.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", "configuration", "environment", "macro"]
|
|
||||||
edition = "2018"
|
|
||||||
license = "MIT"
|
|
||||||
repository = "https://github.com/pleshevskiy/itconfig-rs"
|
|
||||||
homepage = "https://github.com/pleshevskiy/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
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
macro = ["itconfig-macro"]
|
|
||||||
|
|
||||||
number = []
|
|
||||||
bool = []
|
|
||||||
vec = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
|
|
||||||
[badges]
|
|
||||||
maintenance = { status = "actively-developed" }
|
|
||||||
|
|
||||||
# https://docs.rs/about
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
use crate::core::EString;
|
|
||||||
use crate::error::Error;
|
|
||||||
use std::convert::{TryFrom, TryInto};
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
pub fn get_env_or_set_default<R>(env_name: &str, default: R) -> Result<R, Error>
|
|
||||||
where
|
|
||||||
R: TryFrom<EString> + std::fmt::Display,
|
|
||||||
{
|
|
||||||
get_env::<R>(env_name).or_else(|err| match err {
|
|
||||||
Error::NotPresent => {
|
|
||||||
let val = default.to_string();
|
|
||||||
env::set_var(env_name, &val);
|
|
||||||
EString::from(val)
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| Error::Parse(default.to_string()))
|
|
||||||
}
|
|
||||||
_ => Err(err),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_env<R>(env_name: &str) -> Result<R, Error>
|
|
||||||
where
|
|
||||||
R: TryFrom<EString>,
|
|
||||||
{
|
|
||||||
env::var(env_name)
|
|
||||||
.map_err(From::from)
|
|
||||||
.map(EString::from)
|
|
||||||
.and_then(|val| {
|
|
||||||
val.clone()
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| Error::Parse(val.to_string()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
#[cfg(feature = "vec")]
|
|
||||||
use crate::core::vec::{CommaVec, SepVec};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_return_variable() {
|
|
||||||
env::set_var("get_env_1", "hello");
|
|
||||||
match get_env::<&str>("get_env_1") {
|
|
||||||
Ok(res) => assert_eq!(res, "hello"),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_throw_no_present_error() {
|
|
||||||
match get_env::<&str>("get_env_2") {
|
|
||||||
Err(Error::NotPresent) => {}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_throw_parse_error() {
|
|
||||||
env::set_var("get_env_3", "-10");
|
|
||||||
match get_env::<u32>("get_env_3") {
|
|
||||||
Err(Error::Parse(orig)) => {
|
|
||||||
assert_eq!(orig, String::from("-10"))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_set_default_if_var_is_no_present() {
|
|
||||||
let orig = 10;
|
|
||||||
match get_env_or_set_default("get_env_4", orig) {
|
|
||||||
Ok(res) => {
|
|
||||||
assert_eq!(res, orig);
|
|
||||||
assert_eq!(env::var("get_env_4").unwrap(), "10");
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "vec")]
|
|
||||||
#[test]
|
|
||||||
fn should_return_var_as_vector() {
|
|
||||||
env::set_var("get_env_5", "1,2,3,4,5");
|
|
||||||
match get_env::<CommaVec<i32>>("get_env_5") {
|
|
||||||
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "vec")]
|
|
||||||
#[test]
|
|
||||||
fn should_throw_parse_vec_error() {
|
|
||||||
env::set_var("get_env_6", "1,2,3,4,5");
|
|
||||||
match get_env::<SepVec<i32, '+'>>("get_env_6") {
|
|
||||||
Err(Error::Parse(orig)) => {
|
|
||||||
assert_eq!(orig, String::from("1,2,3,4,5"))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "vec")]
|
|
||||||
#[test]
|
|
||||||
fn should_set_default_vector_if_var_is_no_present() {
|
|
||||||
let orig = CommaVec::from(vec![1, 2, 3, 4]);
|
|
||||||
match get_env_or_set_default("get_env_7", orig.clone()) {
|
|
||||||
Ok(res) => {
|
|
||||||
assert_eq!(res, orig);
|
|
||||||
assert_eq!(env::var("get_env_7").unwrap(), "1,2,3,4");
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::EnvString;
|
use crate::core::EString;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
@ -42,16 +42,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const SEP: char> TryFrom<EnvString> for SepVec<T, SEP>
|
impl<T, const SEP: char> TryFrom<EString> for SepVec<T, SEP>
|
||||||
where
|
where
|
||||||
T: TryFrom<EnvString> + std::fmt::Display,
|
T: TryFrom<EString> + std::fmt::Display,
|
||||||
{
|
{
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
|
||||||
fn try_from(value: EnvString) -> Result<Self, Self::Error> {
|
fn try_from(value: EString) -> Result<Self, Self::Error> {
|
||||||
let inner = value
|
let inner = value
|
||||||
.split(SEP)
|
.split(SEP)
|
||||||
.map(EnvString::from)
|
.map(EString::from)
|
||||||
.map(T::try_from)
|
.map(T::try_from)
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
Ok(Self(inner))
|
Ok(Self(inner))
|
182
src/utils.rs
Normal file
182
src/utils.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
use crate::core::EString;
|
||||||
|
use crate::error::Error;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
pub fn get_env_or_set_default<R>(env_name: &str, default: R) -> Result<R, Error>
|
||||||
|
where
|
||||||
|
R: TryFrom<EString> + std::fmt::Display,
|
||||||
|
{
|
||||||
|
get_env::<R>(env_name).or_else(|err| match err {
|
||||||
|
Error::NotPresent => {
|
||||||
|
let val = default.to_string();
|
||||||
|
env::set_var(env_name, &val);
|
||||||
|
EString::from(val)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::Parse(default.to_string()))
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_env<R>(env_name: &str) -> Result<R, Error>
|
||||||
|
where
|
||||||
|
R: TryFrom<EString>,
|
||||||
|
{
|
||||||
|
env::var(env_name)
|
||||||
|
.map_err(From::from)
|
||||||
|
.map(EString::from)
|
||||||
|
.and_then(|val| {
|
||||||
|
val.clone()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::Parse(val.to_string()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct TestCase<const N: u8>;
|
||||||
|
|
||||||
|
impl<const N: u8> std::fmt::Display for TestCase<N> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "get_env_{}", N)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_variable() {
|
||||||
|
let en = TestCase::<1>.to_string();
|
||||||
|
env::set_var(&en, "hello");
|
||||||
|
match get_env::<&str>(&en) {
|
||||||
|
Ok(res) => assert_eq!(res, "hello"),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_throw_no_present_error() {
|
||||||
|
let en = TestCase::<2>.to_string();
|
||||||
|
match get_env::<&str>(&en) {
|
||||||
|
Err(Error::NotPresent) => {}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_set_default_if_var_is_no_present() {
|
||||||
|
let en = TestCase::<3>.to_string();
|
||||||
|
let orig = "hello";
|
||||||
|
match get_env_or_set_default(&en, orig) {
|
||||||
|
Ok(res) => {
|
||||||
|
assert_eq!(res, orig);
|
||||||
|
assert_eq!(env::var(&en).unwrap(), orig);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "number")]
|
||||||
|
mod numbers {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_throw_parse_error() {
|
||||||
|
let en = TestCase::<4>.to_string();
|
||||||
|
env::set_var(&en, "-10");
|
||||||
|
match get_env::<u32>(&en) {
|
||||||
|
Err(Error::Parse(orig)) => {
|
||||||
|
assert_eq!(orig, String::from("-10"))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_set_default_num_if_var_is_no_present() {
|
||||||
|
let en = TestCase::<5>.to_string();
|
||||||
|
let orig = 10;
|
||||||
|
match get_env_or_set_default(&en, orig) {
|
||||||
|
Ok(res) => {
|
||||||
|
assert_eq!(res, orig);
|
||||||
|
assert_eq!(env::var(&en).unwrap(), "10");
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bool")]
|
||||||
|
mod boolean {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_bool_variable() {
|
||||||
|
let en = TestCase::<5>.to_string();
|
||||||
|
|
||||||
|
[
|
||||||
|
("y", true),
|
||||||
|
("yes", true),
|
||||||
|
("true", true),
|
||||||
|
("t", true),
|
||||||
|
("on", true),
|
||||||
|
("false", false),
|
||||||
|
("f", false),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.for_each(|(val, expected)| {
|
||||||
|
let mut en = en.clone();
|
||||||
|
en.push_str(val.as_ref());
|
||||||
|
|
||||||
|
env::set_var(&en, val);
|
||||||
|
match get_env::<bool>(&en) {
|
||||||
|
Ok(res) => assert_eq!(res, *expected),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "vec")]
|
||||||
|
mod vector {
|
||||||
|
use super::*;
|
||||||
|
use crate::core::vec::{CommaVec, SepVec};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_var_as_vector() {
|
||||||
|
let en = TestCase::<6>.to_string();
|
||||||
|
|
||||||
|
env::set_var(&en, "1,2,3,4,5");
|
||||||
|
match get_env::<CommaVec<i32>>(&en) {
|
||||||
|
Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_throw_parse_vec_error() {
|
||||||
|
let en = TestCase::<7>.to_string();
|
||||||
|
env::set_var(&en, "1,2,3,4,5");
|
||||||
|
match get_env::<SepVec<i32, '+'>>(&en) {
|
||||||
|
Err(Error::Parse(orig)) => {
|
||||||
|
assert_eq!(orig, String::from("1,2,3,4,5"))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_set_default_vector_if_var_is_no_present() {
|
||||||
|
let en = TestCase::<8>.to_string();
|
||||||
|
let orig = CommaVec::from(vec![1, 2, 3, 4]);
|
||||||
|
match get_env_or_set_default(&en, orig.clone()) {
|
||||||
|
Ok(res) => {
|
||||||
|
assert_eq!(res, orig);
|
||||||
|
assert_eq!(env::var(&en).unwrap(), "1,2,3,4");
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue