refac: improve core design
- removed all functions, features and examples - recreate core types - recreate get_env and get_env_or_set_default
This commit is contained in:
parent
cf03c58364
commit
21e6aba270
15 changed files with 301 additions and 2219 deletions
3
.vim/coc-settings.json
Normal file
3
.vim/coc-settings.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rust-analyzer.cargo.features": "all"
|
||||||
|
}
|
1336
Cargo.lock
generated
1336
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,43 +0,0 @@
|
||||||
use std::convert::Infallible;
|
|
||||||
|
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
|
||||||
use hyper::{Body, Request, Response, Server};
|
|
||||||
|
|
||||||
itconfig::config! {
|
|
||||||
hyper {
|
|
||||||
static HOST < (
|
|
||||||
ADDR => "127.0.0.1",
|
|
||||||
":",
|
|
||||||
PORT => 8000,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
||||||
Ok(Response::new(Body::from("Hello World!")))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
||||||
config::init();
|
|
||||||
pretty_env_logger::init();
|
|
||||||
|
|
||||||
// For every connection, we must make a `Service` to handle all
|
|
||||||
// incoming HTTP requests on said connection.
|
|
||||||
let make_svc = make_service_fn(|_conn| {
|
|
||||||
// This is the `Service` that will handle the connection.
|
|
||||||
// `service_fn` is a helper to convert a function that
|
|
||||||
// returns a Response into a `Service`.
|
|
||||||
async { Ok::<_, Infallible>(service_fn(hello)) }
|
|
||||||
});
|
|
||||||
|
|
||||||
let addr = config::hyper::HOST().parse()?;
|
|
||||||
|
|
||||||
let server = Server::bind(&addr).serve(make_svc);
|
|
||||||
|
|
||||||
println!("Listening on http://{}", addr);
|
|
||||||
|
|
||||||
server.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Hyper "hello world"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo run --example hyper
|
|
||||||
```
|
|
||||||
|
|
||||||
# Rocket "hello world"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo run --example roket
|
|
||||||
```
|
|
|
@ -1,22 +0,0 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate rocket;
|
|
||||||
|
|
||||||
itconfig::config! {
|
|
||||||
rocket {
|
|
||||||
HOST: String => "localhost",
|
|
||||||
PORT: u16 => 9000,
|
|
||||||
BASE_URL => "/",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/")]
|
|
||||||
fn hello() -> &'static str {
|
|
||||||
"Hello, world!"
|
|
||||||
}
|
|
||||||
|
|
||||||
#[launch]
|
|
||||||
fn rocket() -> _ {
|
|
||||||
config::init();
|
|
||||||
|
|
||||||
rocket::build().mount(config::rocket::BASE_URL(), routes![hello])
|
|
||||||
}
|
|
|
@ -15,52 +15,19 @@ readme = "../README.md"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["primitives"]
|
default = []
|
||||||
|
|
||||||
macro = ["itconfig-macro"]
|
macro = ["itconfig-macro"]
|
||||||
|
|
||||||
primitives = ["numbers", "bool"]
|
number = []
|
||||||
numbers = ["int", "uint", "float"]
|
|
||||||
int = ["i8", "i16", "i32", "i64", "i128", "isize"]
|
|
||||||
uint = ["u8", "u16", "u32", "u64", "u128", "usize"]
|
|
||||||
float = ["f32", "f64"]
|
|
||||||
|
|
||||||
i8 = []
|
|
||||||
i16 = []
|
|
||||||
i32 = []
|
|
||||||
i64 = []
|
|
||||||
i128 = []
|
|
||||||
isize = []
|
|
||||||
|
|
||||||
u8 = []
|
|
||||||
u16 = []
|
|
||||||
u32 = []
|
|
||||||
u64 = []
|
|
||||||
u128 = []
|
|
||||||
usize = []
|
|
||||||
|
|
||||||
f32 = []
|
|
||||||
f64 = []
|
|
||||||
|
|
||||||
bool = []
|
bool = []
|
||||||
|
vec = []
|
||||||
# deprecated since 1.1
|
|
||||||
json_array = ["serde_json"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = { version = "1", optional = true }
|
|
||||||
itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true }
|
itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
# required for examples
|
|
||||||
rocket = "0.5.0-rc.2"
|
|
||||||
hyper = { version = "0.14.4", features = ["full"] }
|
|
||||||
serde_json = "1.0.62"
|
|
||||||
tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread"] }
|
|
||||||
bytes = "1.0.1"
|
|
||||||
futures-util = { version = "0.3.13", default-features = false }
|
|
||||||
pretty_env_logger = "0.4.0"
|
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "passively-maintained" }
|
maintenance = { status = "passively-maintained" }
|
||||||
|
@ -69,13 +36,3 @@ maintenance = { status = "passively-maintained" }
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "hyper"
|
|
||||||
path = "../examples/hyper.rs"
|
|
||||||
required-features = ["macro"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "rocket"
|
|
||||||
path = "../examples/rocket.rs"
|
|
||||||
required-features = ["macro"]
|
|
||||||
|
|
||||||
|
|
56
itconfig/src/core.rs
Normal file
56
itconfig/src/core.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#[cfg(any(feature = "number", feature = "bool"))]
|
||||||
|
pub mod prim;
|
||||||
|
#[cfg(any(feature = "number", feature = "bool"))]
|
||||||
|
pub use prim::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "vec")]
|
||||||
|
pub mod vec;
|
||||||
|
#[cfg(feature = "vec")]
|
||||||
|
pub use vec::*;
|
||||||
|
|
||||||
|
use std::convert::{Infallible, TryFrom};
|
||||||
|
|
||||||
|
/// Wrapper under String type.
|
||||||
|
///
|
||||||
|
/// When we read the environment variable, we automatically convert the value
|
||||||
|
/// to EnvString and then convert it to your expected type.
|
||||||
|
///
|
||||||
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
|
pub struct EString(String);
|
||||||
|
|
||||||
|
impl<T> From<T> for EString
|
||||||
|
where
|
||||||
|
T: std::fmt::Display,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn from(val: T) -> Self {
|
||||||
|
Self(val.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for EString {
|
||||||
|
type Target = String;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<EString> for String {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(s: EString) -> Result<Self, Self::Error> {
|
||||||
|
Ok(s.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<EString> for &'static str {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(s: EString) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Box::leak(s.0.into_boxed_str()))
|
||||||
|
}
|
||||||
|
}
|
39
itconfig/src/core/prim.rs
Normal file
39
itconfig/src/core/prim.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use crate::core::EString;
|
||||||
|
use std::convert::{Infallible, TryFrom};
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
macro_rules! from_env_string_numbers_impl {
|
||||||
|
($($ty:ty),+$(,)?) => {
|
||||||
|
$(
|
||||||
|
#[cfg(feature = "number")]
|
||||||
|
impl TryFrom<EString> for $ty {
|
||||||
|
type Error = <$ty as std::str::FromStr>::Err;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(s: EString) -> Result<Self, Self::Error> {
|
||||||
|
s.0.parse::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
from_env_string_numbers_impl![
|
||||||
|
i8, i16, i32, i64, i128, isize,
|
||||||
|
u8, u16, u32, u64, u128, usize,
|
||||||
|
f32, f64
|
||||||
|
];
|
||||||
|
|
||||||
|
#[cfg(feature = "bool")]
|
||||||
|
impl TryFrom<EString> for bool {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(s: EString) -> Result<Self, Self::Error> {
|
||||||
|
Ok(matches!(
|
||||||
|
s.to_lowercase().as_str(),
|
||||||
|
"true" | "t" | "yes" | "y" | "on" | "1"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
59
itconfig/src/core/vec.rs
Normal file
59
itconfig/src/core/vec.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::core::EnvString;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub const COMMA: char = ',';
|
||||||
|
pub const SEMI: char = ';';
|
||||||
|
|
||||||
|
pub type CommaVec<T> = SepVec<T, COMMA>;
|
||||||
|
pub type SemiVec<T> = SepVec<T, SEMI>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct SepVec<T, const SEP: char>(pub Vec<T>);
|
||||||
|
|
||||||
|
impl<T, const SEP: char> std::ops::Deref for SepVec<T, SEP> {
|
||||||
|
type Target = Vec<T>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const SEP: char> From<Vec<T>> for SepVec<T, SEP> {
|
||||||
|
#[inline]
|
||||||
|
fn from(vec: Vec<T>) -> Self {
|
||||||
|
Self(vec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const SEP: char> std::fmt::Display for SepVec<T, SEP>
|
||||||
|
where
|
||||||
|
T: std::fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.iter().enumerate().try_for_each(|(i, part)| {
|
||||||
|
if i != 0 {
|
||||||
|
f.write_char(SEP)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
f.write_str(&part.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, const SEP: char> TryFrom<EnvString> for SepVec<T, SEP>
|
||||||
|
where
|
||||||
|
T: TryFrom<EnvString> + std::fmt::Display,
|
||||||
|
{
|
||||||
|
type Error = T::Error;
|
||||||
|
|
||||||
|
fn try_from(value: EnvString) -> Result<Self, Self::Error> {
|
||||||
|
let inner = value
|
||||||
|
.split(SEP)
|
||||||
|
.map(EnvString::from)
|
||||||
|
.map(T::try_from)
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(Self(inner))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,176 +0,0 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
/// Wrapper under String type.
|
|
||||||
///
|
|
||||||
/// When we read the environment variable, we automatically convert the value
|
|
||||||
/// to EnvString and then convert it to your expected type.
|
|
||||||
///
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
|
||||||
pub struct EnvString(String);
|
|
||||||
|
|
||||||
impl<T> From<T> for EnvString
|
|
||||||
where
|
|
||||||
T: ToEnvString,
|
|
||||||
{
|
|
||||||
fn from(val: T) -> Self {
|
|
||||||
val.to_env_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for EnvString {
|
|
||||||
type Target = String;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for converting value to EnvString.
|
|
||||||
///
|
|
||||||
/// This trait automatically implemented for any type which implements the
|
|
||||||
/// [`Display`] trait. As such, `ToEnvString` shouldn't be implemented directly:
|
|
||||||
/// [`Display`] should be implemented instead, and you get the `ToEnvString`
|
|
||||||
/// implementation for free.
|
|
||||||
///
|
|
||||||
/// [`Display`]: std::fmt::Display
|
|
||||||
pub trait ToEnvString {
|
|
||||||
/// Converts the giving value to a `EnvString`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// basic usage
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use itconfig::{EnvString, ToEnvString};
|
|
||||||
/// let i = 5;
|
|
||||||
/// let five = EnvString::from("5");
|
|
||||||
/// assert_eq!(five, i.to_env_string());
|
|
||||||
/// ```
|
|
||||||
fn to_env_string(&self) -> EnvString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple and safe type conversions that may fail in a controlled way under
|
|
||||||
/// some circumstances.
|
|
||||||
///
|
|
||||||
/// This trait automatically implemented for all standard primitives. If you
|
|
||||||
/// want to use your custom type in the library you need to implement
|
|
||||||
/// `ToEnvString` and `FromEnvString` manually.
|
|
||||||
pub trait FromEnvString: Sized {
|
|
||||||
/// The type returned in the event of a conversion error.
|
|
||||||
type Err;
|
|
||||||
|
|
||||||
/// Performs the conversion.
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ToEnvString for T
|
|
||||||
where
|
|
||||||
T: std::fmt::Display,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn to_env_string(&self) -> EnvString {
|
|
||||||
EnvString(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
macro_rules! from_env_string_numbers_impl {
|
|
||||||
($($ty:ty => $feature:expr),+) => {
|
|
||||||
$(
|
|
||||||
#[cfg(feature = $feature)]
|
|
||||||
impl FromEnvString for $ty {
|
|
||||||
type Err = <$ty as std::str::FromStr>::Err;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
s.0.parse::<Self>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
from_env_string_numbers_impl![
|
|
||||||
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"
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg(feature = "bool")]
|
|
||||||
impl FromEnvString for bool {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"true" | "t" | "yes" | "y" | "on" | "1" => Ok(true),
|
|
||||||
_ => Ok(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromEnvString for String {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
Ok(s.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromEnvString for &'static str {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
Ok(Box::leak(s.0.clone().into_boxed_str()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//===========================================================================//
|
|
||||||
// DEPRECATED //
|
|
||||||
//===========================================================================//
|
|
||||||
|
|
||||||
/// Error type for json array implementation
|
|
||||||
#[cfg(feature = "json_array")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[deprecated(since = "1.1.0")]
|
|
||||||
pub enum ArrayEnvError {
|
|
||||||
/// Invalid type.
|
|
||||||
InvalidType,
|
|
||||||
|
|
||||||
/// Failed to parse environment variable
|
|
||||||
FailedToParse,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "json_array")]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<T> FromEnvString for Vec<T>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
type Err = ArrayEnvError;
|
|
||||||
|
|
||||||
fn from_env_string(s: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
serde_json::from_str::<Vec<isize>>(s.trim())
|
|
||||||
.map(|vec| vec.iter().map(|v| v.to_string()).collect::<Vec<String>>())
|
|
||||||
.or_else(|_| serde_json::from_str::<Vec<String>>(s.trim()))
|
|
||||||
.map_err(|_| ArrayEnvError::InvalidType)
|
|
||||||
.and_then(|vec| {
|
|
||||||
vec.iter()
|
|
||||||
.map(|v| {
|
|
||||||
FromEnvString::from_env_string(&v.to_env_string())
|
|
||||||
.map_err(|_| ArrayEnvError::FailedToParse)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<T>, _>>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +1,47 @@
|
||||||
|
use std::env::VarError;
|
||||||
use std::error;
|
use std::error;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// The error type for operations interacting with environment variables
|
/// The error type for operations interacting with environment variables
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum EnvError {
|
pub enum Error {
|
||||||
/// The specified environment variable was not present in the current process's environment.
|
/// The specified environment variable was not present in the current process's environment.
|
||||||
MissingVariable(String),
|
NotPresent,
|
||||||
|
|
||||||
/// Failed to parse the specified environment variable.
|
/// Failed to parse the specified environment variable.
|
||||||
FailedToParse(String),
|
Parse(String),
|
||||||
|
|
||||||
|
/// The specified environment variable was found, but it did not contain
|
||||||
|
/// valid unicode data. The found data is returned as a payload of this
|
||||||
|
/// variant.
|
||||||
|
Invalid(OsString),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for EnvError {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
use Error::*;
|
||||||
match &self {
|
match &self {
|
||||||
EnvError::MissingVariable(env_name) => {
|
NotPresent => f.write_str("The specified env variable was not present"),
|
||||||
write!(f, r#"Environment variable "{}" is missing"#, env_name)
|
Invalid(inner) => write!(
|
||||||
}
|
f,
|
||||||
EnvError::FailedToParse(env_name) => {
|
"The specified env variable was found, but it did not valid: '{:?}'",
|
||||||
|
inner,
|
||||||
|
),
|
||||||
|
Parse(env_name) => {
|
||||||
write!(f, r#"Failed to parse environment variable "{}""#, env_name)
|
write!(f, r#"Failed to parse environment variable "{}""#, env_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for EnvError {}
|
impl error::Error for Error {}
|
||||||
|
|
||||||
|
impl From<VarError> for Error {
|
||||||
|
fn from(err: VarError) -> Self {
|
||||||
|
match err {
|
||||||
|
VarError::NotPresent => Error::NotPresent,
|
||||||
|
VarError::NotUnicode(inner) => Error::Invalid(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
use crate::envstr::*;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::utils::*;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
/// Same as get_env but returns Option enum instead Result
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::maybe_get_env;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// env::set_var("HOST", "https://example.com");
|
|
||||||
///
|
|
||||||
/// let host: Option<&'static str> = maybe_get_env("HOST");
|
|
||||||
/// let not_existence_host: Option<&'static str> = maybe_get_env("NOT_EXISTENCE_HOST");
|
|
||||||
///
|
|
||||||
/// assert_eq!(host, Some("https://example.com"));
|
|
||||||
/// assert_eq!(not_existence_host, None);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn maybe_get_env<T>(env_name: &str) -> Option<T>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_env(env_name).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_env`, but it unwraps result with panic on error.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if environment variable is missing or cannot parse variable to
|
|
||||||
/// expected type
|
|
||||||
///
|
|
||||||
pub fn get_env_or_panic<T>(env_name: &str) -> T
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_env(env_name).unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to read environment variable and parse to expected type. You may to put to argument
|
|
||||||
/// any type with `FromEnvString` trait.
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_env;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// env::set_var("DEBUG", "true");
|
|
||||||
///
|
|
||||||
/// let result: bool = get_env("DEBUG").unwrap();
|
|
||||||
///
|
|
||||||
/// assert_eq!(result, true);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_env<T>(env_name: &str) -> Result<T, EnvError>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_env_or(env_name, |_| {
|
|
||||||
Err(EnvError::MissingVariable(env_name.to_string()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_env_or_panic`, but you can pass default value for
|
|
||||||
/// environment variable with `ToEnvString` trait.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if cannot parse variable to expected type
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_env_or_default;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// let result: bool = get_env_or_default("TESTING", "true");
|
|
||||||
/// assert_eq!(result, true);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_env_or_default<T, D>(env_name: &str, default: D) -> T
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
D: ToEnvString,
|
|
||||||
{
|
|
||||||
get_env_or(env_name, |_| Ok(default.to_env_string())).unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_env_or_default`, but the default value will be set to environment
|
|
||||||
/// variable, if env variable is missed.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if cannot parse variable to expected type
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_env_or_set_default;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// let result: bool = get_env_or_set_default("TESTING", "true");
|
|
||||||
/// assert_eq!(result, true);
|
|
||||||
///
|
|
||||||
/// let var = env::var("TESTING").unwrap();
|
|
||||||
/// assert_eq!(var, "true");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_env_or_set_default<T, D>(env_name: &str, default: D) -> T
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
D: ToEnvString,
|
|
||||||
{
|
|
||||||
get_env_or(env_name, |_| {
|
|
||||||
let val = default.to_env_string();
|
|
||||||
env::set_var(env_name, val.as_str());
|
|
||||||
Ok(val)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
|
||||||
/// default expression. Callback should return `EnvString` value or `EnvError`
|
|
||||||
pub fn get_env_or<T, F>(env_name: &str, cb: F) -> Result<T, EnvError>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
F: FnOnce(env::VarError) -> Result<EnvString, EnvError>,
|
|
||||||
{
|
|
||||||
env::var(env_name)
|
|
||||||
.map(|s| s.to_env_string())
|
|
||||||
.or_else(cb)
|
|
||||||
.and_then(|env_str| parse_env_variable(env_name, env_str))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Environment variable \"TEST_CASE_1\" is missing")]
|
|
||||||
fn get_missing_env() {
|
|
||||||
get_env_or_panic::<String>("TEST_CASE_1");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_2\"")]
|
|
||||||
fn get_env_with_invalid_value() {
|
|
||||||
let env_name = "TEST_CASE_2";
|
|
||||||
env::set_var(&env_name, "30r");
|
|
||||||
get_env_or_panic::<u32>(env_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_missing_env() {
|
|
||||||
let env_name = String::from("TEST_CASE_3");
|
|
||||||
let env_val = get_env::<String>(&env_name);
|
|
||||||
assert_eq!(env_val, Err(EnvError::MissingVariable(env_name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_env_with_invalid_value() {
|
|
||||||
let env_name = String::from("TEST_CASE_4");
|
|
||||||
env::set_var(&env_name, "30r");
|
|
||||||
let env_val = get_env::<u32>(&env_name);
|
|
||||||
assert_eq!(env_val, Err(EnvError::FailedToParse(env_name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_env_successfully() {
|
|
||||||
env::set_var("TEST_CASE_5", "30");
|
|
||||||
let env_var = get_env("TEST_CASE_5");
|
|
||||||
assert_eq!(env_var, Ok(30));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_missing_env_with_default_value() {
|
|
||||||
let flag: bool = get_env_or_default("TEST_CASE_6", "true");
|
|
||||||
assert!(flag);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_7\"")]
|
|
||||||
fn get_invalid_env_with_default_value() {
|
|
||||||
env::set_var("TEST_CASE_7", "30r");
|
|
||||||
get_env_or_default::<u32, _>("TEST_CASE_7", 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_8\"")]
|
|
||||||
fn get_env_with_invalid_default_value() {
|
|
||||||
get_env_or_default::<u32, _>("TEST_CASE_8", "30r");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_env_with_default_successfully() {
|
|
||||||
env::set_var("TEST_CASE_9", "10");
|
|
||||||
let env_val: u32 = get_env_or_default("TEST_CASE_9", 30);
|
|
||||||
assert_eq!(env_val, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_missing_env_with_set_default_value() {
|
|
||||||
let flag: bool = get_env_or_set_default("TEST_CASE_10", "true");
|
|
||||||
assert!(flag);
|
|
||||||
|
|
||||||
let env_var = env::var("TEST_CASE_10");
|
|
||||||
assert_eq!(env_var, Ok(String::from("true")))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_optional_env() {
|
|
||||||
env::set_var("TEST_CASE_11", "something");
|
|
||||||
let something: Option<&'static str> = maybe_get_env("TEST_CASE_11");
|
|
||||||
assert_eq!(something, Some("something"));
|
|
||||||
|
|
||||||
let nothing: Option<&'static str> = maybe_get_env("TEST_CASE_11_NONE");
|
|
||||||
assert_eq!(nothing, None);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,304 +0,0 @@
|
||||||
use crate::envstr::*;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::utils::*;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
/// Same as get_vec_env but returns Option enum instead Result
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::*;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, PartialEq, Eq)]
|
|
||||||
/// enum PaymentPlatform {
|
|
||||||
/// PayPal,
|
|
||||||
/// Stripe,
|
|
||||||
/// SomethingElse,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl FromEnvString for PaymentPlatform {
|
|
||||||
/// type Err = &'static str;
|
|
||||||
///
|
|
||||||
/// fn from_env_string(envstr: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
/// match envstr.to_lowercase().as_str() {
|
|
||||||
/// "paypal" => Ok(Self::PayPal),
|
|
||||||
/// "stripe" => Ok(Self::Stripe),
|
|
||||||
/// "smth" => Ok(Self::SomethingElse),
|
|
||||||
/// _ => Err("Unsupported payment platform"),
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// env::set_var("PAYMENT_PLATFORMS", "paypal,stripe");
|
|
||||||
///
|
|
||||||
/// let payment_platforms: Option<Vec<PaymentPlatform>> = maybe_get_vec_env("PAYMENT_PLATFORMS", ",");
|
|
||||||
/// assert_eq!(
|
|
||||||
/// payment_platforms,
|
|
||||||
/// Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe])
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn maybe_get_vec_env<T>(env_name: &str, sep: &'static str) -> Option<Vec<T>>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_vec_env(env_name, sep).ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_vec_env`, but it unwraps result with panic on error.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if environment variable is missing or cannot parse variable to
|
|
||||||
/// expected type
|
|
||||||
///
|
|
||||||
pub fn get_vec_env_or_panic<T>(env_name: &str, sep: &'static str) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_vec_env(env_name, sep).unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to read environment variable, split by separator and parse each item to expected
|
|
||||||
/// type.
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_vec_env;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// env::set_var("DEBUG", "true");
|
|
||||||
///
|
|
||||||
/// let result: Vec<bool> = get_vec_env("DEBUG", ",").unwrap();
|
|
||||||
///
|
|
||||||
/// assert_eq!(result, vec![true]);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_vec_env<T>(env_name: &str, sep: &str) -> Result<Vec<T>, EnvError>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
{
|
|
||||||
get_vec_env_or(env_name, sep, |_| {
|
|
||||||
Err(EnvError::MissingVariable(env_name.to_string()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_vec_env_or_panic`, but you can pass default value for
|
|
||||||
/// environment variable with `ToEnvString` trait.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if cannot parse variable to expected type
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_vec_env_or_default;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// let result: Vec<bool> = get_vec_env_or_default("TESTING", ",", vec!["true"]);
|
|
||||||
/// assert_eq!(result, vec![true]);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_vec_env_or_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
D: ToEnvString,
|
|
||||||
{
|
|
||||||
get_vec_env_or(env_name, sep, |_| Ok(vec_to_env_strings(default))).unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is similar as `get_vec_env_or_default`, but the default value will be set to environment
|
|
||||||
/// variable, if env variable is missed.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ------
|
|
||||||
/// Application will panic if cannot parse variable to expected type
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// -------
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # extern crate itconfig;
|
|
||||||
/// # use itconfig::get_vec_env_or_set_default;
|
|
||||||
/// use std::env;
|
|
||||||
///
|
|
||||||
/// fn main () {
|
|
||||||
/// let result: Vec<bool> = get_vec_env_or_set_default("TESTING", ",", vec!["true"]);
|
|
||||||
/// assert_eq!(result, vec![true]);
|
|
||||||
///
|
|
||||||
/// let var = env::var("TESTING").unwrap();
|
|
||||||
/// assert_eq!(var, "true");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_vec_env_or_set_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
D: ToEnvString,
|
|
||||||
{
|
|
||||||
get_vec_env_or(env_name, sep, |_| {
|
|
||||||
let default_env_strings = vec_to_env_strings(default);
|
|
||||||
let env_val = join(&default_env_strings, sep);
|
|
||||||
env::set_var(env_name, env_val.as_str());
|
|
||||||
Ok(default_env_strings)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(make_panic)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
|
||||||
/// default expression. Callback should return `EnvString` value or `EnvError`
|
|
||||||
pub fn get_vec_env_or<T, F>(env_name: &str, sep: &str, cb: F) -> Result<Vec<T>, EnvError>
|
|
||||||
where
|
|
||||||
T: FromEnvString,
|
|
||||||
F: FnOnce(env::VarError) -> Result<Vec<EnvString>, EnvError>,
|
|
||||||
{
|
|
||||||
env::var(env_name)
|
|
||||||
.map(|s| {
|
|
||||||
s.split(sep)
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| item.to_env_string())
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.or_else(cb)
|
|
||||||
.and_then(|items| {
|
|
||||||
items
|
|
||||||
.into_iter()
|
|
||||||
.map(|env_str| parse_env_variable(env_name, env_str))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const SEP: &str = ",";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Environment variable \"TEST_CASE_VEC_1\" is missing")]
|
|
||||||
fn get_missing_vec_env() {
|
|
||||||
let _: Vec<&'static str> = get_vec_env_or_panic("TEST_CASE_VEC_1", SEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_2\"")]
|
|
||||||
fn get_vec_env_with_invalid_value() {
|
|
||||||
let env_name = "TEST_CASE_VEC_2";
|
|
||||||
env::set_var(&env_name, "30r");
|
|
||||||
let _: Vec<u32> = get_vec_env_or_panic(env_name, SEP);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_missing_vec_env() {
|
|
||||||
let env_name = String::from("TEST_CASE_VEC_3");
|
|
||||||
let env_val = get_vec_env::<String>(&env_name, SEP);
|
|
||||||
assert_eq!(env_val, Err(EnvError::MissingVariable(env_name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_vec_env_with_invalid_value() {
|
|
||||||
let env_name = String::from("TEST_CASE_VEC_4");
|
|
||||||
env::set_var(&env_name, "30r");
|
|
||||||
let env_val = get_vec_env::<u32>(&env_name, SEP);
|
|
||||||
assert_eq!(env_val, Err(EnvError::FailedToParse(env_name)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_result_of_vec_env_successfully() {
|
|
||||||
env::set_var("TEST_CASE_VEC_5", "30");
|
|
||||||
let env_var = get_vec_env("TEST_CASE_VEC_5", SEP);
|
|
||||||
assert_eq!(env_var, Ok(vec![30]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_missing_vec_env_with_default_value() {
|
|
||||||
let flag: Vec<bool> = get_vec_env_or_default("TEST_CASE_VEC_6", SEP, vec!["true"]);
|
|
||||||
assert_eq!(flag, vec![true]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_7\"")]
|
|
||||||
fn get_invalid_vec_env_with_default_value() {
|
|
||||||
env::set_var("TEST_CASE_VEC_7", "30r");
|
|
||||||
get_vec_env_or_default::<u32, _>("TEST_CASE_VEC_7", SEP, vec![30]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_8\"")]
|
|
||||||
fn get_vec_env_with_invalid_default_value() {
|
|
||||||
get_vec_env_or_default::<u32, _>("TEST_CASE_VEC_8", SEP, vec!["30r"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_vec_env_with_default_successfully() {
|
|
||||||
env::set_var("TEST_CASE_VEC_9", "10");
|
|
||||||
let env_val: Vec<u32> = get_vec_env_or_default("TEST_CASE_VEC_9", SEP, vec![30]);
|
|
||||||
assert_eq!(env_val, vec![10])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_missing_vec_env_with_set_default_value() {
|
|
||||||
let flag: Vec<bool> = get_vec_env_or_set_default("TEST_CASE_VEC_10", SEP, vec!["true"]);
|
|
||||||
assert_eq!(flag, vec![true]);
|
|
||||||
|
|
||||||
let env_var = env::var("TEST_CASE_VEC_10");
|
|
||||||
assert_eq!(env_var, Ok(String::from("true")))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_optional_vec_env() {
|
|
||||||
env::set_var("TEST_CASE_VEC_11", "something");
|
|
||||||
let something: Option<Vec<&'static str>> = maybe_get_vec_env("TEST_CASE_VEC_11", SEP);
|
|
||||||
assert_eq!(something, Some(vec!["something"]));
|
|
||||||
|
|
||||||
let nothing: Option<Vec<&'static str>> = maybe_get_vec_env("TEST_CASE_VEC_11_NONE", SEP);
|
|
||||||
assert_eq!(nothing, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_custom_type_from_vec_env() {
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
enum PaymentPlatform {
|
|
||||||
PayPal,
|
|
||||||
Stripe,
|
|
||||||
SomethingElse,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromEnvString for PaymentPlatform {
|
|
||||||
type Err = &'static str;
|
|
||||||
|
|
||||||
fn from_env_string(envstr: &EnvString) -> Result<Self, Self::Err> {
|
|
||||||
match envstr.to_lowercase().as_str() {
|
|
||||||
"paypal" => Ok(Self::PayPal),
|
|
||||||
"stripe" => Ok(Self::Stripe),
|
|
||||||
"smth" => Ok(Self::SomethingElse),
|
|
||||||
_ => Err("Unsupported payment platform"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
env::set_var("TEST_CASE_VEC_12", "paypal,stripe");
|
|
||||||
let something: Option<Vec<PaymentPlatform>> = maybe_get_vec_env("TEST_CASE_VEC_12", SEP);
|
|
||||||
assert_eq!(
|
|
||||||
something,
|
|
||||||
Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -137,7 +137,7 @@
|
||||||
#![forbid(non_ascii_idents)]
|
#![forbid(non_ascii_idents)]
|
||||||
#![deny(
|
#![deny(
|
||||||
missing_debug_implementations,
|
missing_debug_implementations,
|
||||||
missing_docs,
|
// missing_docs,
|
||||||
unstable_features,
|
unstable_features,
|
||||||
unused_imports,
|
unused_imports,
|
||||||
unused_qualifications
|
unused_qualifications
|
||||||
|
@ -148,16 +148,13 @@
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
mod envstr;
|
pub mod core;
|
||||||
mod error;
|
mod error;
|
||||||
mod get_env;
|
mod utils;
|
||||||
mod get_vec_env;
|
|
||||||
pub(crate) mod utils;
|
|
||||||
|
|
||||||
pub use self::envstr::*;
|
pub use self::core::*;
|
||||||
pub use self::error::*;
|
pub use self::error::*;
|
||||||
pub use self::get_env::*;
|
pub use self::utils::*;
|
||||||
pub use self::get_vec_env::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "macro")]
|
#[cfg(feature = "macro")]
|
||||||
extern crate itconfig_macro;
|
extern crate itconfig_macro;
|
||||||
|
|
|
@ -1,33 +1,116 @@
|
||||||
use crate::{EnvError, EnvString, FromEnvString, ToEnvString};
|
use crate::core::EString;
|
||||||
|
use crate::error::Error;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
pub(crate) fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
pub fn get_env_or_set_default<R>(env_name: &str, default: R) -> Result<R, Error>
|
||||||
where
|
where
|
||||||
T: FromEnvString,
|
R: TryFrom<EString> + std::fmt::Display,
|
||||||
{
|
{
|
||||||
FromEnvString::from_env_string(&env_str)
|
get_env::<R>(env_name).or_else(|err| match err {
|
||||||
.map_err(|_| EnvError::FailedToParse(env_name.to_string()))
|
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(crate) fn make_panic<T>(e: EnvError) -> T {
|
|
||||||
panic!("{}", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn join(env_strings: &[EnvString], sep: &str) -> String {
|
|
||||||
env_strings
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.fold(String::new(), |mut res, (i, item)| {
|
|
||||||
if i > 0 {
|
|
||||||
res.push_str(sep);
|
|
||||||
}
|
|
||||||
res.push_str(item);
|
|
||||||
res
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn vec_to_env_strings<T>(values: Vec<T>) -> Vec<EnvString>
|
pub fn get_env<R>(env_name: &str) -> Result<R, Error>
|
||||||
where
|
where
|
||||||
T: ToEnvString,
|
R: TryFrom<EString>,
|
||||||
{
|
{
|
||||||
values.into_iter().map(EnvString::from).collect()
|
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!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue