From 13ac65527714f51e13b659bcaca1cd79b0993a98 Mon Sep 17 00:00:00 2001 From: Ruslan Mikhaylov Date: Fri, 29 Sep 2023 04:16:17 +0300 Subject: [PATCH] feat: custom key value separator (#284) * feat: custom key value separator * feat: custom key value separator Separate test for key value separator * feat: custom key value separator Update README.md --- README.md | 5 ++++- env.go | 9 +++++++-- env_test.go | 14 +++++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d2836e7..16acbc6 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,10 @@ If you set the `envDefault` tag for something, this value will be used in the case of absence of it in the environment. By default, slice types will split the environment value on `,`; you can change -this behavior by setting the `envSeparator` tag. +this behavior by setting the `envSeparator` tag. For map types, the default +separator between key and value is `:` and `,` for key-value pairs. +The behavior can be changed by setting the `envKeyValSeparator` and +`envSeparator` tags accordingly. ## Custom Parser Funcs diff --git a/env.go b/env.go index 62758d5..01a5455 100644 --- a/env.go +++ b/env.go @@ -518,11 +518,16 @@ func handleMap(field reflect.Value, value string, sf reflect.StructField, funcMa separator = "," } + keyValSeparator := sf.Tag.Get("envKeyValSeparator") + if keyValSeparator == "" { + keyValSeparator = ":" + } + result := reflect.MakeMap(sf.Type) for _, part := range strings.Split(value, separator) { - pairs := strings.Split(part, ":") + pairs := strings.Split(part, keyValSeparator) if len(pairs) != 2 { - return newParseError(sf, fmt.Errorf(`%q should be in "key:value" format`, part)) + return newParseError(sf, fmt.Errorf(`%q should be in "key%svalue" format`, part, keyValSeparator)) } key, err := keyParserFunc(pairs[0]) diff --git a/env_test.go b/env_test.go index 74cbed4..e491008 100644 --- a/env_test.go +++ b/env_test.go @@ -403,9 +403,10 @@ func TestParsesEnv(t *testing.T) { func TestParsesEnv_Map(t *testing.T) { type config struct { - MapStringString map[string]string `env:"MAP_STRING_STRING" envSeparator:","` - MapStringInt64 map[string]int64 `env:"MAP_STRING_INT64"` - MapStringBool map[string]bool `env:"MAP_STRING_BOOL" envSeparator:";"` + MapStringString map[string]string `env:"MAP_STRING_STRING" envSeparator:","` + MapStringInt64 map[string]int64 `env:"MAP_STRING_INT64"` + MapStringBool map[string]bool `env:"MAP_STRING_BOOL" envSeparator:";"` + CustomSeparatorMapStringString map[string]string `env:"CUSTOM_SEPARATOR_MAP_STRING_STRING" envSeparator:"," envKeyValSeparator:"|"` } mss := map[string]string{ @@ -426,12 +427,19 @@ func TestParsesEnv_Map(t *testing.T) { } t.Setenv("MAP_STRING_BOOL", "k1:true;k2:false") + withCustomSeparator := map[string]string{ + "k1": "v1", + "k2": "v2", + } + t.Setenv("CUSTOM_SEPARATOR_MAP_STRING_STRING", "k1|v1,k2|v2") + var cfg config isNoErr(t, Parse(&cfg)) isEqual(t, mss, cfg.MapStringString) isEqual(t, msi, cfg.MapStringInt64) isEqual(t, msb, cfg.MapStringBool) + isEqual(t, withCustomSeparator, cfg.CustomSeparatorMapStringString) } func TestParsesEnvInvalidMap(t *testing.T) {