From 3ac95bcf830b6d73f9ec4775bcf9962b850c8e1f Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Sat, 26 Nov 2022 15:38:29 +0300 Subject: [PATCH] support multiple env params for single value Signed-off-by: Vasiliy Tolstov --- env.go | 29 +++++++++++------ env_test.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 11 deletions(-) diff --git a/env.go b/env.go index 0a86e72..80b5099 100644 --- a/env.go +++ b/env.go @@ -219,13 +219,14 @@ func (c *envConfig) setValues(ctx context.Context, valueOf reflect.Value) error continue } - tag, ok := field.Tag.Lookup(c.opts.StructTag) + tags, ok := field.Tag.Lookup(c.opts.StructTag) if !ok { continue } - - if err := os.Setenv(tag, fmt.Sprintf("%v", value.Interface())); err != nil && !c.opts.AllowFail { - return err + for _, tag := range strings.Split(tags, ",") { + if err := os.Setenv(tag, fmt.Sprintf("%v", value.Interface())); err != nil && !c.opts.AllowFail { + return err + } } } @@ -278,16 +279,24 @@ func fillValues(ctx context.Context, valueOf reflect.Value, structTag string) er } continue } - tag, ok := field.Tag.Lookup(structTag) - if !ok { - continue - } - val, ok := os.LookupEnv(tag) + tags, ok := field.Tag.Lookup(structTag) if !ok { continue } - if err := fillValue(ctx, value, val); err != nil { + var eval string + for _, tag := range strings.Split(tags, ",") { + if val, ok := os.LookupEnv(tag); !ok { + continue + } else { + eval = val + } + } + if eval == "" { + continue + } + + if err := fillValue(ctx, value, eval); err != nil { return err } } diff --git a/env_test.go b/env_test.go index 8f09dff..63d4afd 100644 --- a/env_test.go +++ b/env_test.go @@ -11,7 +11,7 @@ import ( ) type Config struct { - StringValue string `env:"STRING_VALUE"` + StringValue string `env:"STRING_VALUE,STRING_VALUE2"` BoolValue bool `env:"BOOL_VALUE"` StringSlice []string `env:"STRING_SLICE"` IntSlice []int `env:"INT_SLICE"` @@ -20,6 +20,14 @@ type Config struct { } func TestMerge(t *testing.T) { + defer func() { + for _, v := range []string{"STRING_VALUE", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } + }() + ctx := context.Background() type Nested struct { Name string `env:"NAME_VALUE"` @@ -47,6 +55,9 @@ func TestMerge(t *testing.T) { t.Fatal(err) } }() + if err != nil { + t.Fatal(err) + } os.Setenv("NAME_VALUE", "after") changes, err := w.Next() @@ -66,6 +77,14 @@ func TestMerge(t *testing.T) { } func TestLoad(t *testing.T) { + defer func() { + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } + }() + ctx := context.Background() conf := &Config{StringValue: "before_load"} cfg := NewConfig(config.Struct(conf)) @@ -111,9 +130,23 @@ func TestLoad(t *testing.T) { if len(conf.MapIntValue) != 2 { t.Fatalf("something wrong with env config: %#+v", conf.MapIntValue) } + + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } } func TestSave(t *testing.T) { + defer func() { + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } + }() + ctx := context.Background() conf := &Config{StringValue: "MICRO_CONFIG_ENV"} cfg := NewConfig(config.Struct(conf)) @@ -149,4 +182,59 @@ func TestSave(t *testing.T) { t.Fatalf("env value %s=%s set", tv, v) } } + + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } +} + +func TestLoadMultiple(t *testing.T) { + defer func() { + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } + }() + + ctx := context.Background() + conf := &Config{StringValue: "before_load"} + cfg := NewConfig(config.Struct(conf)) + + if err := cfg.Init(); err != nil { + t.Fatal(err) + } + + if err := cfg.Load(ctx, config.LoadOverride(true), config.LoadAppend(true)); err != nil { + t.Fatal(err) + } + + if conf.StringValue != "before_load" { + t.Fatalf("something wrong with env config: %#+v", conf) + } + + os.Setenv("STRING_VALUE", "STRING_VALUE1") + os.Setenv("STRING_VALUE2", "STRING_VALUE2") + defer func() { + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } + }() + + if err := cfg.Load(ctx, config.LoadOverride(true), config.LoadAppend(true)); err != nil { + t.Fatal(err) + } + if conf.StringValue != "STRING_VALUE2" { + t.Fatalf("something wrong with env config: %#+v", conf) + } + + for _, v := range []string{"STRING_VALUE", "STRING_VALUE2", "BOOL_VALUE", "STRING_SLICE", "INT_SLICE", "MAP_STRING", "MAP_INT"} { + if err := os.Unsetenv(v); err != nil { + t.Fatal(err) + } + } }