From 70b670b07c477700f13a89bf4422e4e26daaa7c2 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Thu, 22 Apr 2021 11:41:46 +0300 Subject: [PATCH] feat: add custom separator attribute --- itconfig-macro/src/ast.rs | 2 ++ itconfig-macro/src/expand.rs | 18 ++++++++-------- itconfig-macro/src/parse.rs | 12 ++++++++++- itconfig-macro/src/utils.rs | 6 +++--- itconfig-tests/Cargo.toml | 2 +- itconfig-tests/tests/config_macro.rs | 25 +++++++++++++++++++++- itconfig/src/get_vec_env.rs | 32 ++++++---------------------- itconfig/src/utils.rs | 22 ++++++++++++++++++- 8 files changed, 78 insertions(+), 41 deletions(-) diff --git a/itconfig-macro/src/ast.rs b/itconfig-macro/src/ast.rs index 98bfec8..4425e34 100644 --- a/itconfig-macro/src/ast.rs +++ b/itconfig-macro/src/ast.rs @@ -1,3 +1,4 @@ +use crate::utils::SupportedBox; use proc_macro2::TokenStream as TokenStream2; use syn::{Attribute, Expr, Ident, Type}; @@ -24,4 +25,5 @@ pub struct Variable { pub concat_parts: Option>, pub env_name: Option, pub meta: Vec, + pub supported_box: Option, } diff --git a/itconfig-macro/src/expand.rs b/itconfig-macro/src/expand.rs index c284628..29eb1d8 100644 --- a/itconfig-macro/src/expand.rs +++ b/itconfig-macro/src/expand.rs @@ -1,5 +1,5 @@ use crate::ast::*; -use crate::utils::{maybe_supported_box, vec_to_token_stream_2, SupportedBox}; +use crate::utils::{vec_to_token_stream_2, SupportedBox}; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens, TokenStreamExt}; @@ -128,8 +128,6 @@ impl ToTokens for Variable { .unwrap_or_else(|| name.to_string().to_uppercase()); let meta = vec_to_token_stream_2(&self.meta); - let supported_box = maybe_supported_box(&ty); - let get_variable: TokenStream2 = if self.concat_parts.is_some() { let concat_parts = self.concat_parts.as_ref().unwrap(); quote! {{ @@ -139,19 +137,21 @@ impl ToTokens for Variable { value }} } else if let Some(initial) = &self.initial { - match supported_box { - Some(SupportedBox::Vec) => { - quote!(::itconfig::get_vec_env_or_set_default(#env_name, ",", #initial)) + match self.supported_box.clone() { + Some(SupportedBox::Vec { sep }) => { + let sep = &sep.unwrap_or_else(|| String::from(",")); + quote!(::itconfig::get_vec_env_or_set_default(#env_name, #sep, #initial)) } _ => quote!(::itconfig::get_env_or_set_default(#env_name, #initial)), } } else { - match supported_box { + match self.supported_box.clone() { Some(SupportedBox::Option) => { quote!(::itconfig::maybe_get_env(#env_name)) } - Some(SupportedBox::Vec) => { - quote!(::itconfig::get_vec_env_or_panic(#env_name, ",")) + Some(SupportedBox::Vec { sep }) => { + let sep = &sep.unwrap_or_else(|| String::from(",")); + quote!(::itconfig::get_vec_env_or_panic(#env_name, #sep)) } _ => { quote!(::itconfig::get_env_or_panic(#env_name)) diff --git a/itconfig-macro/src/parse.rs b/itconfig-macro/src/parse.rs index 9a1f923..9551de4 100644 --- a/itconfig-macro/src/parse.rs +++ b/itconfig-macro/src/parse.rs @@ -1,4 +1,5 @@ use crate::ast::*; +use crate::utils::{maybe_supported_box, SupportedBox}; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; use syn::ext::IdentExt; @@ -73,7 +74,13 @@ fn parse_namespace_content( if attr.path.is_ident("env_name") { variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?; } else { - variable.meta.push(attr); + match variable.supported_box { + Some(SupportedBox::Vec { sep: current_sep }) if attr.path.is_ident("sep") => { + let sep = parse_attribute(attr, "sep", ¤t_sep)?; + variable.supported_box = Some(SupportedBox::Vec { sep }); + } + _ => variable.meta.push(attr), + } } } @@ -220,6 +227,8 @@ impl Parse for Variable { parse_str("&'static str")? }; + let supported_box = maybe_supported_box(&ty); + if is_concat { input.parse::()?; @@ -263,6 +272,7 @@ impl Parse for Variable { input.parse::().ok(); Ok(Variable { + supported_box, is_static, name, ty, diff --git a/itconfig-macro/src/utils.rs b/itconfig-macro/src/utils.rs index ebc0fa2..6aa7812 100644 --- a/itconfig-macro/src/utils.rs +++ b/itconfig-macro/src/utils.rs @@ -5,9 +5,9 @@ use syn::{Path, Type}; const OPTION_PATH_IDENTS: &[&str] = &["Option|", "std|option|Option|", "core|option|Option|"]; const VEC_PATH_IDENTS: &[&str] = &["Vec|", "std|vec|Vec|"]; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum SupportedBox { - Vec, + Vec { sep: Option }, Option, } @@ -44,7 +44,7 @@ pub fn maybe_supported_box(ty: &Type) -> Option { if is_option_path_ident(&path_ident) { Some(SupportedBox::Option) } else if is_vec_path_ident(&path_ident) { - Some(SupportedBox::Vec) + Some(SupportedBox::Vec { sep: None }) } else { None } diff --git a/itconfig-tests/Cargo.toml b/itconfig-tests/Cargo.toml index be42141..7a72838 100644 --- a/itconfig-tests/Cargo.toml +++ b/itconfig-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "itconfig_tests" -version = "0.1.1" +version = "0.1.0" authors = ["Dmitriy Pleshevskiy "] edition = "2018" license = "MIT" diff --git a/itconfig-tests/tests/config_macro.rs b/itconfig-tests/tests/config_macro.rs index 551328d..233e2fe 100644 --- a/itconfig-tests/tests/config_macro.rs +++ b/itconfig-tests/tests/config_macro.rs @@ -506,6 +506,7 @@ mod test_case_22 { "static ", STATIC_CONCAT_PART => "part", ), + static STATIC_VEC: Vec => 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]); } } @@ -573,3 +575,24 @@ mod test_case_24 { 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"]); + } +} diff --git a/itconfig/src/get_vec_env.rs b/itconfig/src/get_vec_env.rs index c045c5c..9096fc5 100644 --- a/itconfig/src/get_vec_env.rs +++ b/itconfig/src/get_vec_env.rs @@ -86,7 +86,7 @@ where /// } /// ``` /// -pub fn get_vec_env(env_name: &str, sep: &'static str) -> Result, EnvError> +pub fn get_vec_env(env_name: &str, sep: &str) -> Result, EnvError> where T: FromEnvString, { @@ -116,15 +116,12 @@ where /// } /// ``` /// -pub fn get_vec_env_or_default(env_name: &str, sep: &'static str, default: Vec) -> Vec +pub fn get_vec_env_or_default(env_name: &str, sep: &str, default: Vec) -> Vec where T: FromEnvString, D: ToEnvString, { - get_vec_env_or(env_name, sep, |_| { - Ok(default.into_iter().map(EnvString::from).collect()) - }) - .unwrap_or_else(make_panic) + 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 @@ -151,29 +148,14 @@ where /// } /// ``` /// -pub fn get_vec_env_or_set_default( - env_name: &str, - sep: &'static str, - default: Vec, -) -> Vec +pub fn get_vec_env_or_set_default(env_name: &str, sep: &str, default: Vec) -> Vec where T: FromEnvString, D: ToEnvString, { get_vec_env_or(env_name, sep, |_| { - let default_env_strings: Vec = - default.into_iter().map(EnvString::from).collect(); - let env_val = - default_env_strings - .iter() - .enumerate() - .fold(String::new(), |mut res, (i, item)| { - if i > 0 { - res.push_str(sep); - } - res.push_str(&item); - res - }); + 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) }) @@ -182,7 +164,7 @@ where /// 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(env_name: &str, sep: &'static str, cb: F) -> Result, EnvError> +pub fn get_vec_env_or(env_name: &str, sep: &str, cb: F) -> Result, EnvError> where T: FromEnvString, F: FnOnce(env::VarError) -> Result, EnvError>, diff --git a/itconfig/src/utils.rs b/itconfig/src/utils.rs index 9e42a11..441beed 100644 --- a/itconfig/src/utils.rs +++ b/itconfig/src/utils.rs @@ -1,4 +1,4 @@ -use crate::{EnvError, EnvString, FromEnvString}; +use crate::{EnvError, EnvString, FromEnvString, ToEnvString}; pub(crate) fn parse_env_variable(env_name: &str, env_str: EnvString) -> Result where @@ -11,3 +11,23 @@ where pub(crate) fn make_panic(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(values: Vec) -> Vec +where + T: ToEnvString, +{ + values.into_iter().map(EnvString::from).collect() +}