feat: add possibility to get optional env

This commit is contained in:
Dmitriy Pleshevskiy 2021-04-15 23:35:57 +03:00
parent e479a1e40e
commit d1982ad8af
4 changed files with 164 additions and 74 deletions

View file

@ -1,6 +1,8 @@
use crate::ast::*; use crate::ast::*;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens, TokenStreamExt}; use quote::{quote, ToTokens, TokenStreamExt};
use syn::Path;
use syn::Type;
fn vec_to_token_stream_2<T>(input: &Vec<T>) -> Vec<TokenStream2> fn vec_to_token_stream_2<T>(input: &Vec<T>) -> Vec<TokenStream2>
where where
@ -145,6 +147,8 @@ impl ToTokens for Variable {
} else if self.initial.is_some() { } else if self.initial.is_some() {
let initial = self.initial.as_ref().unwrap(); let initial = self.initial.as_ref().unwrap();
quote!(::itconfig::get_env_or_set_default(#env_name, #initial)) 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 { } else {
quote!(::itconfig::get_env_or_panic(#env_name)) quote!(::itconfig::get_env_or_panic(#env_name))
}; };
@ -170,3 +174,30 @@ impl ToTokens for Variable {
} }
} }
} }
fn path_ident(path: &Path) -> String {
path.segments
.iter()
.into_iter()
.fold(String::with_capacity(250), |mut acc, v| {
acc.push_str(&v.ident.to_string());
acc.push('|');
acc
})
}
fn is_option_path_ident(path_ident: String) -> bool {
vec!["Option|", "std|option|Option|", "core|option|Option|"]
.into_iter()
.find(|s| &path_ident == *s)
.is_some()
}
fn is_option_type(ty: &Type) -> bool {
match ty {
Type::Path(ty_path) => {
ty_path.qself.is_none() && is_option_path_ident(path_ident(&ty_path.path))
}
_ => false,
}
}

View file

@ -531,3 +531,22 @@ mod test_case_22 {
assert_eq!(config::STATIC_CONCAT_VARIABLE(), "static part".to_string()) assert_eq!(config::STATIC_CONCAT_VARIABLE(), "static part".to_string())
} }
} }
mod test_case_23 {
use std::env;
itconfig::config! {
SOMETHING: Option<&'static str>,
NOTHING: Option<&'static str>,
}
#[test]
fn optional_variables() {
config::init();
env::set_var("SOMETHING", "hello world");
assert_eq!(config::SOMETHING(), Some("hello world"));
assert_eq!(config::NOTHING(), None);
}
}

View file

@ -1,74 +0,0 @@
use itconfig::EnvError::*;
use itconfig::*;
use std::env;
#[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(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(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_eq!(flag, true);
}
#[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_eq!(flag, true);
let env_var = env::var("TEST_CASE_10");
assert_eq!(env_var, Ok(String::from("true")))
}

View file

@ -1,6 +1,34 @@
use crate::prelude::*; use crate::prelude::*;
use std::env; 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. /// This function is similar as `get_env`, but it unwraps result with panic on error.
/// ///
/// Panics /// Panics
@ -137,3 +165,89 @@ where
fn make_panic<T>(e: EnvError) -> T { fn make_panic<T>(e: EnvError) -> T {
panic!("{}", e) panic!("{}", e)
} }
#[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_eq!(flag, true);
}
#[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_eq!(flag, true);
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);
}
}