feat: add custom separator attribute
This commit is contained in:
parent
5d6b335a7a
commit
70b670b07c
8 changed files with 78 additions and 41 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::utils::SupportedBox;
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use syn::{Attribute, Expr, Ident, Type};
|
use syn::{Attribute, Expr, Ident, Type};
|
||||||
|
|
||||||
|
@ -24,4 +25,5 @@ pub struct Variable {
|
||||||
pub concat_parts: Option<Vec<TokenStream2>>,
|
pub concat_parts: Option<Vec<TokenStream2>>,
|
||||||
pub env_name: Option<String>,
|
pub env_name: Option<String>,
|
||||||
pub meta: Vec<Attribute>,
|
pub meta: Vec<Attribute>,
|
||||||
|
pub supported_box: Option<SupportedBox>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ast::*;
|
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 proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
|
||||||
|
@ -128,8 +128,6 @@ impl ToTokens for Variable {
|
||||||
.unwrap_or_else(|| name.to_string().to_uppercase());
|
.unwrap_or_else(|| name.to_string().to_uppercase());
|
||||||
let meta = vec_to_token_stream_2(&self.meta);
|
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 get_variable: TokenStream2 = if self.concat_parts.is_some() {
|
||||||
let concat_parts = self.concat_parts.as_ref().unwrap();
|
let concat_parts = self.concat_parts.as_ref().unwrap();
|
||||||
quote! {{
|
quote! {{
|
||||||
|
@ -139,19 +137,21 @@ impl ToTokens for Variable {
|
||||||
value
|
value
|
||||||
}}
|
}}
|
||||||
} else if let Some(initial) = &self.initial {
|
} else if let Some(initial) = &self.initial {
|
||||||
match supported_box {
|
match self.supported_box.clone() {
|
||||||
Some(SupportedBox::Vec) => {
|
Some(SupportedBox::Vec { sep }) => {
|
||||||
quote!(::itconfig::get_vec_env_or_set_default(#env_name, ",", #initial))
|
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)),
|
_ => quote!(::itconfig::get_env_or_set_default(#env_name, #initial)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match supported_box {
|
match self.supported_box.clone() {
|
||||||
Some(SupportedBox::Option) => {
|
Some(SupportedBox::Option) => {
|
||||||
quote!(::itconfig::maybe_get_env(#env_name))
|
quote!(::itconfig::maybe_get_env(#env_name))
|
||||||
}
|
}
|
||||||
Some(SupportedBox::Vec) => {
|
Some(SupportedBox::Vec { sep }) => {
|
||||||
quote!(::itconfig::get_vec_env_or_panic(#env_name, ","))
|
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))
|
quote!(::itconfig::get_env_or_panic(#env_name))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
use crate::utils::{maybe_supported_box, SupportedBox};
|
||||||
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
|
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
|
@ -73,7 +74,13 @@ fn parse_namespace_content(
|
||||||
if attr.path.is_ident("env_name") {
|
if attr.path.is_ident("env_name") {
|
||||||
variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?;
|
variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?;
|
||||||
} else {
|
} 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")?
|
parse_str("&'static str")?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let supported_box = maybe_supported_box(&ty);
|
||||||
|
|
||||||
if is_concat {
|
if is_concat {
|
||||||
input.parse::<Lt>()?;
|
input.parse::<Lt>()?;
|
||||||
|
|
||||||
|
@ -263,6 +272,7 @@ impl Parse for Variable {
|
||||||
input.parse::<Comma>().ok();
|
input.parse::<Comma>().ok();
|
||||||
|
|
||||||
Ok(Variable {
|
Ok(Variable {
|
||||||
|
supported_box,
|
||||||
is_static,
|
is_static,
|
||||||
name,
|
name,
|
||||||
ty,
|
ty,
|
||||||
|
|
|
@ -5,9 +5,9 @@ use syn::{Path, Type};
|
||||||
const OPTION_PATH_IDENTS: &[&str] = &["Option|", "std|option|Option|", "core|option|Option|"];
|
const OPTION_PATH_IDENTS: &[&str] = &["Option|", "std|option|Option|", "core|option|Option|"];
|
||||||
const VEC_PATH_IDENTS: &[&str] = &["Vec|", "std|vec|Vec|"];
|
const VEC_PATH_IDENTS: &[&str] = &["Vec|", "std|vec|Vec|"];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SupportedBox {
|
pub enum SupportedBox {
|
||||||
Vec,
|
Vec { sep: Option<String> },
|
||||||
Option,
|
Option,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ pub fn maybe_supported_box(ty: &Type) -> Option<SupportedBox> {
|
||||||
if is_option_path_ident(&path_ident) {
|
if is_option_path_ident(&path_ident) {
|
||||||
Some(SupportedBox::Option)
|
Some(SupportedBox::Option)
|
||||||
} else if is_vec_path_ident(&path_ident) {
|
} else if is_vec_path_ident(&path_ident) {
|
||||||
Some(SupportedBox::Vec)
|
Some(SupportedBox::Vec { sep: None })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "itconfig_tests"
|
name = "itconfig_tests"
|
||||||
version = "0.1.1"
|
version = "0.1.0"
|
||||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -506,6 +506,7 @@ mod test_case_22 {
|
||||||
"static ",
|
"static ",
|
||||||
STATIC_CONCAT_PART => "part",
|
STATIC_CONCAT_PART => "part",
|
||||||
),
|
),
|
||||||
|
static STATIC_VEC: Vec<u32> => vec![1],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -528,7 +529,8 @@ mod test_case_22 {
|
||||||
assert_eq!(config::STATIC_USIZE(), 1);
|
assert_eq!(config::STATIC_USIZE(), 1);
|
||||||
assert_eq!(config::STATIC_F32(), 1.0);
|
assert_eq!(config::STATIC_F32(), 1.0);
|
||||||
assert_eq!(config::STATIC_F64(), 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"]);
|
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"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ where
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn get_vec_env<T>(env_name: &str, sep: &'static str) -> Result<Vec<T>, EnvError>
|
pub fn get_vec_env<T>(env_name: &str, sep: &str) -> Result<Vec<T>, EnvError>
|
||||||
where
|
where
|
||||||
T: FromEnvString,
|
T: FromEnvString,
|
||||||
{
|
{
|
||||||
|
@ -116,15 +116,12 @@ where
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
pub fn get_vec_env_or_default<T, D>(env_name: &str, sep: &'static str, default: Vec<D>) -> Vec<T>
|
pub fn get_vec_env_or_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
||||||
where
|
where
|
||||||
T: FromEnvString,
|
T: FromEnvString,
|
||||||
D: ToEnvString,
|
D: ToEnvString,
|
||||||
{
|
{
|
||||||
get_vec_env_or(env_name, sep, |_| {
|
get_vec_env_or(env_name, sep, |_| Ok(vec_to_env_strings(default))).unwrap_or_else(make_panic)
|
||||||
Ok(default.into_iter().map(EnvString::from).collect())
|
|
||||||
})
|
|
||||||
.unwrap_or_else(make_panic)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is similar as `get_vec_env_or_default`, but the default value will be set to environment
|
/// 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<T, D>(
|
pub fn get_vec_env_or_set_default<T, D>(env_name: &str, sep: &str, default: Vec<D>) -> Vec<T>
|
||||||
env_name: &str,
|
|
||||||
sep: &'static str,
|
|
||||||
default: Vec<D>,
|
|
||||||
) -> Vec<T>
|
|
||||||
where
|
where
|
||||||
T: FromEnvString,
|
T: FromEnvString,
|
||||||
D: ToEnvString,
|
D: ToEnvString,
|
||||||
{
|
{
|
||||||
get_vec_env_or(env_name, sep, |_| {
|
get_vec_env_or(env_name, sep, |_| {
|
||||||
let default_env_strings: Vec<EnvString> =
|
let default_env_strings = vec_to_env_strings(default);
|
||||||
default.into_iter().map(EnvString::from).collect();
|
let env_val = join(&default_env_strings, sep);
|
||||||
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
|
|
||||||
});
|
|
||||||
env::set_var(env_name, env_val.as_str());
|
env::set_var(env_name, env_val.as_str());
|
||||||
Ok(default_env_strings)
|
Ok(default_env_strings)
|
||||||
})
|
})
|
||||||
|
@ -182,7 +164,7 @@ where
|
||||||
|
|
||||||
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
/// This function returns env variable as `EnvString` structure. You can pass callback for custom
|
||||||
/// default expression. Callback should return `EnvString` value or `EnvError`
|
/// default expression. Callback should return `EnvString` value or `EnvError`
|
||||||
pub fn get_vec_env_or<T, F>(env_name: &str, sep: &'static str, cb: F) -> Result<Vec<T>, EnvError>
|
pub fn get_vec_env_or<T, F>(env_name: &str, sep: &str, cb: F) -> Result<Vec<T>, EnvError>
|
||||||
where
|
where
|
||||||
T: FromEnvString,
|
T: FromEnvString,
|
||||||
F: FnOnce(env::VarError) -> Result<Vec<EnvString>, EnvError>,
|
F: FnOnce(env::VarError) -> Result<Vec<EnvString>, EnvError>,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{EnvError, EnvString, FromEnvString};
|
use crate::{EnvError, EnvString, FromEnvString, ToEnvString};
|
||||||
|
|
||||||
pub(crate) fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
pub(crate) fn parse_env_variable<T>(env_name: &str, env_str: EnvString) -> Result<T, EnvError>
|
||||||
where
|
where
|
||||||
|
@ -11,3 +11,23 @@ where
|
||||||
pub(crate) fn make_panic<T>(e: EnvError) -> T {
|
pub(crate) fn make_panic<T>(e: EnvError) -> T {
|
||||||
panic!("{}", e)
|
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