From 4e0574e7d4e3a8972a6ee41a537f82a8b20ce8e8 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Sun, 5 Apr 2020 20:12:32 +0200 Subject: [PATCH] envconst.Var --- util/envconst/envconst.go | 31 +++++++++++++++++++++ util/envconst/envconst_test.go | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 util/envconst/envconst_test.go diff --git a/util/envconst/envconst.go b/util/envconst/envconst.go index 8b8e56f..8c965fb 100644 --- a/util/envconst/envconst.go +++ b/util/envconst/envconst.go @@ -1,7 +1,10 @@ package envconst import ( + "flag" + "fmt" "os" + "reflect" "strconv" "sync" "time" @@ -85,3 +88,31 @@ func String(varname string, def string) string { cache.Store(varname, e) return e } + +func Var(varname string, def flag.Value) interface{} { + + // use def's type to instantiate a new object of that same type + // and call flag.Value.Set() on it + defType := reflect.TypeOf(def) + if defType.Kind() != reflect.Ptr { + panic(fmt.Sprintf("envconst var must be a pointer, got %T", def)) + } + defElemType := defType.Elem() + + if v, ok := cache.Load(varname); ok { + return v.(string) + } + e := os.Getenv(varname) + if e == "" { + return def + } + + newInstance := reflect.New(defElemType) + if err := newInstance.Interface().(flag.Value).Set(e); err != nil { + panic(err) + } + + res := newInstance.Interface() + cache.Store(varname, res) + return res +} diff --git a/util/envconst/envconst_test.go b/util/envconst/envconst_test.go new file mode 100644 index 0000000..bc4af67 --- /dev/null +++ b/util/envconst/envconst_test.go @@ -0,0 +1,50 @@ +package envconst_test + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/zrepl/zrepl/util/envconst" +) + +type ExampleVarType struct{ string } + +var ( + Var1 = ExampleVarType{"var1"} + Var2 = ExampleVarType{"var2"} +) + +func (m ExampleVarType) String() string { return string(m.string) } +func (m *ExampleVarType) Set(s string) error { + switch s { + case Var1.String(): + *m = Var1 + case Var2.String(): + *m = Var2 + default: + return fmt.Errorf("unknown var %q", s) + } + return nil +} + +const EnvVarName = "ZREPL_ENVCONST_UNIT_TEST_VAR" + +func TestVar(t *testing.T) { + _, set := os.LookupEnv(EnvVarName) + require.False(t, set) + defer os.Unsetenv(EnvVarName) + + val := envconst.Var(EnvVarName, &Var1) + if &Var1 != val { + t.Errorf("default value shut be same address") + } + + err := os.Setenv(EnvVarName, "var2") + require.NoError(t, err) + + val = envconst.Var(EnvVarName, &Var1) + require.Equal(t, &Var2, val, "only structural identity is required for non-default vars") +}