diff --git a/itconfig-macro/src/expand.rs b/itconfig-macro/src/expand.rs index 29eb1d8..3aa9dae 100644 --- a/itconfig-macro/src/expand.rs +++ b/itconfig-macro/src/expand.rs @@ -138,7 +138,7 @@ impl ToTokens for Variable { }} } else if let Some(initial) = &self.initial { match self.supported_box.clone() { - Some(SupportedBox::Vec { sep }) => { + Some(SupportedBox::Vec(sep)) => { let sep = &sep.unwrap_or_else(|| String::from(",")); quote!(::itconfig::get_vec_env_or_set_default(#env_name, #sep, #initial)) } @@ -149,11 +149,15 @@ impl ToTokens for Variable { Some(SupportedBox::Option) => { quote!(::itconfig::maybe_get_env(#env_name)) } - Some(SupportedBox::Vec { sep }) => { + Some(SupportedBox::OptionVec(sep)) => { + let sep = &sep.unwrap_or_else(|| String::from(",")); + quote!(::itconfig::maybe_get_vec_env(#env_name, #sep)) + } + Some(SupportedBox::Vec(sep)) => { let sep = &sep.unwrap_or_else(|| String::from(",")); quote!(::itconfig::get_vec_env_or_panic(#env_name, #sep)) } - _ => { + None => { quote!(::itconfig::get_env_or_panic(#env_name)) } } diff --git a/itconfig-macro/src/parse.rs b/itconfig-macro/src/parse.rs index 9551de4..9755b1a 100644 --- a/itconfig-macro/src/parse.rs +++ b/itconfig-macro/src/parse.rs @@ -75,9 +75,9 @@ fn parse_namespace_content( variable.env_name = parse_attribute(attr, "env_name", &variable.env_name)?; } else { match variable.supported_box { - Some(SupportedBox::Vec { sep: current_sep }) if attr.path.is_ident("sep") => { + Some(SupportedBox::Vec(current_sep)) if attr.path.is_ident("sep") => { let sep = parse_attribute(attr, "sep", ¤t_sep)?; - variable.supported_box = Some(SupportedBox::Vec { sep }); + variable.supported_box = Some(SupportedBox::Vec(sep)); } _ => variable.meta.push(attr), } diff --git a/itconfig-macro/src/utils.rs b/itconfig-macro/src/utils.rs index 6aa7812..1c4d71a 100644 --- a/itconfig-macro/src/utils.rs +++ b/itconfig-macro/src/utils.rs @@ -1,14 +1,15 @@ 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|"]; #[derive(Debug, Clone)] pub enum SupportedBox { - Vec { sep: Option }, + Vec(Option), Option, + OptionVec(Option), } pub fn vec_to_token_stream_2(input: &[T]) -> Vec @@ -40,11 +41,24 @@ fn is_vec_path_ident(path_ident: &str) -> bool { pub fn maybe_supported_box(ty: &Type) -> Option { match ty { Type::Path(ty_path) if ty_path.qself.is_none() => { - let path_ident = path_ident(&ty_path.path); - if is_option_path_ident(&path_ident) { - Some(SupportedBox::Option) - } else if is_vec_path_ident(&path_ident) { - Some(SupportedBox::Vec { sep: None }) + let ty_path_ident = path_ident(&ty_path.path); + if is_option_path_ident(&ty_path_ident) { + match &ty_path.path.segments.iter().last().unwrap().arguments { + PathArguments::AngleBracketed(params) => match params.args.first() { + Some(GenericArgument::Type(Type::Path(inner_ty_path))) => { + let ty_path_ident = path_ident(&inner_ty_path.path); + if is_vec_path_ident(&ty_path_ident) { + Some(SupportedBox::OptionVec(None)) + } else { + Some(SupportedBox::Option) + } + } + _ => Some(SupportedBox::Option), + }, + _ => Some(SupportedBox::Option), + } + } else if is_vec_path_ident(&ty_path_ident) { + Some(SupportedBox::Vec(None)) } else { None } diff --git a/itconfig-tests/tests/config_macro.rs b/itconfig-tests/tests/config_macro.rs index 233e2fe..e521d85 100644 --- a/itconfig-tests/tests/config_macro.rs +++ b/itconfig-tests/tests/config_macro.rs @@ -596,3 +596,18 @@ mod test_case_25 { assert_eq!(config::CUSTOM_SEP_STD_VEC(), vec!["paypal", "stripe"]); } } + +mod test_case_26 { + use std::env; + + itconfig::config! { + OPTION_VEC: Option>, + } + + #[test] + fn optional_vec() { + env::set_var("OPTION_VEC", "paypal,stripe"); + + assert_eq!(config::OPTION_VEC(), Some(vec!["paypal", "stripe"])); + } +}