commit
43eb463918
10 changed files with 508 additions and 44 deletions
|
@ -1,14 +1,15 @@
|
|||
use crate::utils::SupportedBox;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn::{Attribute, Expr, Ident, Type};
|
||||
|
||||
pub struct RootNamespace {
|
||||
pub(crate) struct RootNamespace {
|
||||
pub name: Option<Ident>,
|
||||
pub variables: Vec<Variable>,
|
||||
pub namespaces: Vec<Namespace>,
|
||||
pub meta: Vec<Attribute>,
|
||||
}
|
||||
|
||||
pub struct Namespace {
|
||||
pub(crate) struct Namespace {
|
||||
pub name: Ident,
|
||||
pub variables: Vec<Variable>,
|
||||
pub namespaces: Vec<Namespace>,
|
||||
|
@ -16,7 +17,7 @@ pub struct Namespace {
|
|||
pub meta: Vec<Attribute>,
|
||||
}
|
||||
|
||||
pub struct Variable {
|
||||
pub(crate) struct Variable {
|
||||
pub is_static: bool,
|
||||
pub name: Ident,
|
||||
pub ty: Type,
|
||||
|
@ -24,4 +25,5 @@ pub struct Variable {
|
|||
pub concat_parts: Option<Vec<TokenStream2>>,
|
||||
pub env_name: Option<String>,
|
||||
pub meta: Vec<Attribute>,
|
||||
pub supported_box: Option<SupportedBox>,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast::*;
|
||||
use crate::utils::{is_option_type, vec_to_token_stream_2};
|
||||
use crate::utils::{vec_to_token_stream_2, SupportedBox};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
|
||||
|
@ -136,13 +136,31 @@ impl ToTokens for Variable {
|
|||
::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 if is_option_type(&self.ty) {
|
||||
quote!(::itconfig::maybe_get_env(#env_name))
|
||||
} else if let Some(initial) = &self.initial {
|
||||
match self.supported_box.clone() {
|
||||
Some(SupportedBox::Vec(params)) => {
|
||||
let sep = ¶ms.sep();
|
||||
quote!(::itconfig::get_vec_env_or_set_default(#env_name, #sep, #initial))
|
||||
}
|
||||
_ => quote!(::itconfig::get_env_or_set_default(#env_name, #initial)),
|
||||
}
|
||||
} else {
|
||||
quote!(::itconfig::get_env_or_panic(#env_name))
|
||||
match self.supported_box.clone() {
|
||||
Some(SupportedBox::Option) => {
|
||||
quote!(::itconfig::maybe_get_env(#env_name))
|
||||
}
|
||||
Some(SupportedBox::OptionVec(params)) => {
|
||||
let sep = ¶ms.sep();
|
||||
quote!(::itconfig::maybe_get_vec_env(#env_name, #sep))
|
||||
}
|
||||
Some(SupportedBox::Vec(params)) => {
|
||||
let sep = ¶ms.sep();
|
||||
quote!(::itconfig::get_vec_env_or_panic(#env_name, #sep))
|
||||
}
|
||||
None => {
|
||||
quote!(::itconfig::get_env_or_panic(#env_name))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if self.is_static {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::ast::*;
|
||||
use crate::utils::{maybe_supported_box, SupportedBox, VecBoxParams};
|
||||
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use syn::ext::IdentExt;
|
||||
|
@ -59,7 +60,8 @@ fn parse_namespace_content(
|
|||
|
||||
for attr in attributes {
|
||||
if attr.path.is_ident("env_prefix") {
|
||||
namespace.env_prefix = parse_attribute(attr, "env_prefix", &namespace.env_prefix)?;
|
||||
let env_prefix = parse_attribute(attr, "env_prefix", &namespace.env_prefix)?;
|
||||
namespace.env_prefix = Some(env_prefix);
|
||||
} else {
|
||||
namespace.meta.push(attr);
|
||||
}
|
||||
|
@ -71,9 +73,17 @@ fn parse_namespace_content(
|
|||
|
||||
for attr in attributes {
|
||||
if attr.path.is_ident("env_name") {
|
||||
variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?;
|
||||
let env_name = parse_attribute(attr, "env_name", &variable.env_name)?;
|
||||
variable.env_name = Some(env_name);
|
||||
} else {
|
||||
variable.meta.push(attr);
|
||||
match variable.supported_box {
|
||||
Some(SupportedBox::Vec(params)) if attr.path.is_ident("sep") => {
|
||||
let sep = parse_attribute(attr, "sep", ¶ms.sep_opt())?;
|
||||
variable.supported_box =
|
||||
Some(SupportedBox::Vec(VecBoxParams::new(Some(sep))));
|
||||
}
|
||||
_ => variable.meta.push(attr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,11 +93,7 @@ fn parse_namespace_content(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_attribute(
|
||||
attr: Attribute,
|
||||
name: &'static str,
|
||||
var: &Option<String>,
|
||||
) -> Result<Option<String>> {
|
||||
fn parse_attribute(attr: Attribute, name: &'static str, var: &Option<String>) -> Result<String> {
|
||||
if var.is_some() {
|
||||
let message = format!("You cannot use {} meta twice", &name);
|
||||
return Err(Error::new_spanned(attr, message));
|
||||
|
@ -97,7 +103,7 @@ fn parse_attribute(
|
|||
Meta::NameValue(MetaNameValue {
|
||||
lit: Lit::Str(lit_str),
|
||||
..
|
||||
}) => Ok(Some(lit_str.value())),
|
||||
}) => Ok(lit_str.value()),
|
||||
_ => {
|
||||
let message = format!("expected #[{} = \"...\"]", &name);
|
||||
Err(Error::new_spanned(attr, message))
|
||||
|
@ -220,6 +226,8 @@ impl Parse for Variable {
|
|||
parse_str("&'static str")?
|
||||
};
|
||||
|
||||
let supported_box = maybe_supported_box(&ty);
|
||||
|
||||
if is_concat {
|
||||
input.parse::<Lt>()?;
|
||||
|
||||
|
@ -263,6 +271,7 @@ impl Parse for Variable {
|
|||
input.parse::<Comma>().ok();
|
||||
|
||||
Ok(Variable {
|
||||
supported_box,
|
||||
is_static,
|
||||
name,
|
||||
ty,
|
||||
|
|
|
@ -1,10 +1,38 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::ToTokens;
|
||||
use syn::{Path, Type};
|
||||
use syn::{GenericArgument, Path, PathArguments, Type};
|
||||
|
||||
const OPTION_PATH_IDENTS: &[&str] = &["Option|", "std|option|Option|", "core|option|Option|"];
|
||||
const VEC_PATH_IDENTS: &[&str] = &["Vec|", "std|vec|Vec|"];
|
||||
|
||||
pub fn vec_to_token_stream_2<T>(input: &[T]) -> Vec<TokenStream2>
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct VecBoxParams(Option<String>);
|
||||
|
||||
impl VecBoxParams {
|
||||
#[inline]
|
||||
pub(crate) fn new(sep: Option<String>) -> Self {
|
||||
VecBoxParams(sep)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sep_opt(&self) -> Option<String> {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sep(&self) -> String {
|
||||
self.0.clone().unwrap_or_else(|| String::from(","))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum SupportedBox {
|
||||
Vec(VecBoxParams),
|
||||
Option,
|
||||
OptionVec(VecBoxParams),
|
||||
}
|
||||
|
||||
pub(crate) fn vec_to_token_stream_2<T>(input: &[T]) -> Vec<TokenStream2>
|
||||
where
|
||||
T: ToTokens,
|
||||
{
|
||||
|
@ -22,15 +50,39 @@ fn path_ident(path: &Path) -> String {
|
|||
})
|
||||
}
|
||||
|
||||
fn is_option_path_ident(path_ident: String) -> bool {
|
||||
fn is_option_path_ident(path_ident: &str) -> bool {
|
||||
OPTION_PATH_IDENTS.iter().any(|s| path_ident == *s)
|
||||
}
|
||||
|
||||
pub fn is_option_type(ty: &Type) -> bool {
|
||||
fn is_vec_path_ident(path_ident: &str) -> bool {
|
||||
VEC_PATH_IDENTS.iter().any(|s| path_ident == *s)
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_supported_box(ty: &Type) -> Option<SupportedBox> {
|
||||
match ty {
|
||||
Type::Path(ty_path) => {
|
||||
ty_path.qself.is_none() && is_option_path_ident(path_ident(&ty_path.path))
|
||||
Type::Path(ty_path) if ty_path.qself.is_none() => {
|
||||
let ty_path_ident = path_ident(&ty_path.path);
|
||||
if is_option_path_ident(&ty_path_ident) {
|
||||
if let PathArguments::AngleBracketed(params) =
|
||||
&ty_path.path.segments.iter().last().unwrap().arguments
|
||||
{
|
||||
if let Some(GenericArgument::Type(Type::Path(inner_ty_path))) =
|
||||
params.args.first()
|
||||
{
|
||||
let ty_path_ident = path_ident(&inner_ty_path.path);
|
||||
if is_vec_path_ident(&ty_path_ident) {
|
||||
return Some(SupportedBox::OptionVec(Default::default()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(SupportedBox::Option)
|
||||
} else if is_vec_path_ident(&ty_path_ident) {
|
||||
Some(SupportedBox::Vec(Default::default()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -506,6 +506,7 @@ mod test_case_22 {
|
|||
"static ",
|
||||
STATIC_CONCAT_PART => "part",
|
||||
),
|
||||
static STATIC_VEC: Vec<u32> => vec![1],
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -528,7 +529,8 @@ mod test_case_22 {
|
|||
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())
|
||||
assert_eq!(config::STATIC_CONCAT_VARIABLE(), "static part".to_string());
|
||||
assert_eq!(config::STATIC_VEC(), vec![1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,8 +549,6 @@ mod test_case_23 {
|
|||
|
||||
#[test]
|
||||
fn optional_variables() {
|
||||
config::init();
|
||||
|
||||
env::set_var("SOMETHING", "hello world");
|
||||
|
||||
assert_eq!(config::SOMETHING(), Some("hello world"));
|
||||
|
@ -557,3 +557,57 @@ mod test_case_23 {
|
|||
assert_eq!(config::NOTHING(), None);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_case_24 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
MY_VEC: Vec<&'static str>,
|
||||
#[env_name = "MY_VEC"]
|
||||
STD_VEC: std::vec::Vec<&'static str>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vector_of_values() {
|
||||
env::set_var("MY_VEC", "paypal,stripe");
|
||||
|
||||
assert_eq!(config::MY_VEC(), vec!["paypal", "stripe"]);
|
||||
assert_eq!(config::STD_VEC(), vec!["paypal", "stripe"]);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_case_25 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
#[sep = ";"]
|
||||
CUSTOM_SEP_MY_VEC: Vec<&'static str>,
|
||||
|
||||
#[env_name = "CUSTOM_SEP_MY_VEC"]
|
||||
#[sep = ";"]
|
||||
CUSTOM_SEP_STD_VEC: std::vec::Vec<&'static str>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_separator_for_vector() {
|
||||
env::set_var("CUSTOM_SEP_MY_VEC", "paypal;stripe");
|
||||
|
||||
assert_eq!(config::CUSTOM_SEP_MY_VEC(), vec!["paypal", "stripe"]);
|
||||
assert_eq!(config::CUSTOM_SEP_STD_VEC(), vec!["paypal", "stripe"]);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_case_26 {
|
||||
use std::env;
|
||||
|
||||
itconfig::config! {
|
||||
OPTION_VEC: Option<Vec<&'static str>>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional_vec() {
|
||||
env::set_var("OPTION_VEC", "paypal,stripe");
|
||||
|
||||
assert_eq!(config::OPTION_VEC(), Some(vec!["paypal", "stripe"]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::ops::Deref;
|
|||
/// When we read the environment variable, we automatically convert the value
|
||||
/// to EnvString and then convert it to your expected type.
|
||||
///
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||
pub struct EnvString(String);
|
||||
|
||||
impl<T> From<T> for EnvString
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::envstr::*;
|
||||
use crate::error::*;
|
||||
use crate::utils::*;
|
||||
use std::env;
|
||||
|
||||
/// Same as get_env but returns Option enum instead Result
|
||||
|
@ -152,18 +153,6 @@ where
|
|||
.and_then(|env_str| parse_env_variable(env_name, env_str))
|
||||
}
|
||||
|
||||
fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
FromEnvString::from_env_string(&env_str)
|
||||
.map_err(|_| EnvError::FailedToParse(env_name.to_string()))
|
||||
}
|
||||
|
||||
fn make_panic<T>(e: EnvError) -> T {
|
||||
panic!("{}", e)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
304
itconfig/src/get_vec_env.rs
Normal file
304
itconfig/src/get_vec_env.rs
Normal file
|
@ -0,0 +1,304 @@
|
|||
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])
|
||||
);
|
||||
}
|
||||
}
|
|
@ -149,11 +149,14 @@
|
|||
|
||||
mod envstr;
|
||||
mod error;
|
||||
mod getenv;
|
||||
mod get_env;
|
||||
mod get_vec_env;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub use self::envstr::*;
|
||||
pub use self::error::*;
|
||||
pub use self::getenv::*;
|
||||
pub use self::get_env::*;
|
||||
pub use self::get_vec_env::*;
|
||||
|
||||
#[cfg(feature = "macro")]
|
||||
extern crate itconfig_macro;
|
||||
|
|
33
itconfig/src/utils.rs
Normal file
33
itconfig/src/utils.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::{EnvError, EnvString, FromEnvString, ToEnvString};
|
||||
|
||||
pub(crate) fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
||||
where
|
||||
T: FromEnvString,
|
||||
{
|
||||
FromEnvString::from_env_string(&env_str)
|
||||
.map_err(|_| EnvError::FailedToParse(env_name.to_string()))
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
T: ToEnvString,
|
||||
{
|
||||
values.into_iter().map(EnvString::from).collect()
|
||||
}
|
Reference in a new issue